mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 21:15:52 +08:00
Surface drag is not dependent on text configuration any more
This commit is contained in:
parent
e72e2f1005
commit
c0f8370bf4
@ -44,7 +44,7 @@ std::optional<Glyph> get_glyph(const stbtt_fontinfo &font_info, int unicode_lett
|
|||||||
const Glyph* get_glyph(int unicode, const FontFile &font, const FontProp &font_prop,
|
const Glyph* get_glyph(int unicode, const FontFile &font, const FontProp &font_prop,
|
||||||
Glyphs &cache, fontinfo_opt &font_info_opt);
|
Glyphs &cache, fontinfo_opt &font_info_opt);
|
||||||
|
|
||||||
EmbossStyle create_style(std::wstring name, std::wstring path);
|
EmbossStyle create_style(const std::wstring &name, const std::wstring &path);
|
||||||
|
|
||||||
// scale and convert float to int coordinate
|
// scale and convert float to int coordinate
|
||||||
Point to_point(const stbtt__point &point);
|
Point to_point(const stbtt__point &point);
|
||||||
@ -797,8 +797,7 @@ const Glyph* priv::get_glyph(
|
|||||||
auto glyph_item = cache.find(unicode);
|
auto glyph_item = cache.find(unicode);
|
||||||
if (glyph_item != cache.end()) return &glyph_item->second;
|
if (glyph_item != cache.end()) return &glyph_item->second;
|
||||||
|
|
||||||
unsigned int font_index = font_prop.collection_number.has_value()?
|
unsigned int font_index = font_prop.collection_number.value_or(0);
|
||||||
*font_prop.collection_number : 0;
|
|
||||||
if (!is_valid(font, font_index)) return nullptr;
|
if (!is_valid(font, font_index)) return nullptr;
|
||||||
|
|
||||||
if (!font_info_opt.has_value()) {
|
if (!font_info_opt.has_value()) {
|
||||||
@ -813,47 +812,43 @@ const Glyph* priv::get_glyph(
|
|||||||
// Fix for very small flatness because it create huge amount of points from curve
|
// Fix for very small flatness because it create huge amount of points from curve
|
||||||
if (flatness < RESOLUTION) flatness = RESOLUTION;
|
if (flatness < RESOLUTION) flatness = RESOLUTION;
|
||||||
|
|
||||||
std::optional<Glyph> glyph_opt =
|
std::optional<Glyph> glyph_opt = priv::get_glyph(*font_info_opt, unicode, flatness);
|
||||||
priv::get_glyph(*font_info_opt, unicode, flatness);
|
|
||||||
|
|
||||||
// IMPROVE: multiple loadig glyph without data
|
// IMPROVE: multiple loadig glyph without data
|
||||||
// has definition inside of font?
|
// has definition inside of font?
|
||||||
if (!glyph_opt.has_value()) return nullptr;
|
if (!glyph_opt.has_value()) return nullptr;
|
||||||
|
|
||||||
|
Glyph &glyph = *glyph_opt;
|
||||||
if (font_prop.char_gap.has_value())
|
if (font_prop.char_gap.has_value())
|
||||||
glyph_opt->advance_width += *font_prop.char_gap;
|
glyph.advance_width += *font_prop.char_gap;
|
||||||
|
|
||||||
// scale glyph size
|
// scale glyph size
|
||||||
glyph_opt->advance_width =
|
glyph.advance_width = static_cast<int>(glyph.advance_width / SHAPE_SCALE);
|
||||||
static_cast<int>(glyph_opt->advance_width / SHAPE_SCALE);
|
glyph.left_side_bearing = static_cast<int>(glyph.left_side_bearing / SHAPE_SCALE);
|
||||||
glyph_opt->left_side_bearing =
|
|
||||||
static_cast<int>(glyph_opt->left_side_bearing / SHAPE_SCALE);
|
|
||||||
|
|
||||||
if (!glyph_opt->shape.empty()) {
|
if (!glyph.shape.empty()) {
|
||||||
if (font_prop.boldness.has_value()) {
|
if (font_prop.boldness.has_value()) {
|
||||||
float delta = *font_prop.boldness / SHAPE_SCALE /
|
float delta = static_cast<float>(*font_prop.boldness / SHAPE_SCALE / font_prop.size_in_mm);
|
||||||
font_prop.size_in_mm;
|
glyph.shape = Slic3r::union_ex(offset_ex(glyph.shape, delta));
|
||||||
glyph_opt->shape = Slic3r::union_ex(offset_ex(glyph_opt->shape, delta));
|
|
||||||
}
|
}
|
||||||
if (font_prop.skew.has_value()) {
|
if (font_prop.skew.has_value()) {
|
||||||
const float &ratio = *font_prop.skew;
|
const float &ratio = *font_prop.skew;
|
||||||
auto skew = [&ratio](Polygon &polygon) {
|
auto skew = [&ratio](Polygon &polygon) {
|
||||||
for (Slic3r::Point &p : polygon.points) {
|
for (Slic3r::Point &p : polygon.points)
|
||||||
p.x() += p.y() * ratio;
|
p.x() += static_cast<int>(std::round(p.y() * ratio));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
for (ExPolygon &expolygon : glyph_opt->shape) {
|
for (ExPolygon &expolygon : glyph.shape) {
|
||||||
skew(expolygon.contour);
|
skew(expolygon.contour);
|
||||||
for (Polygon &hole : expolygon.holes) skew(hole);
|
for (Polygon &hole : expolygon.holes) skew(hole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto it = cache.insert({unicode, std::move(*glyph_opt)});
|
auto [it, success] = cache.try_emplace(unicode, std::move(glyph));
|
||||||
assert(it.second);
|
assert(success);
|
||||||
return &it.first->second;
|
return &it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
EmbossStyle priv::create_style(std::wstring name, std::wstring path) {
|
EmbossStyle priv::create_style(const std::wstring& name, const std::wstring& path) {
|
||||||
return { boost::nowide::narrow(name.c_str()),
|
return { boost::nowide::narrow(name.c_str()),
|
||||||
boost::nowide::narrow(path.c_str()),
|
boost::nowide::narrow(path.c_str()),
|
||||||
EmbossStyle::Type::file_path, FontProp() };
|
EmbossStyle::Type::file_path, FontProp() };
|
||||||
@ -1220,8 +1215,7 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache,
|
|||||||
Point cursor(0, 0);
|
Point cursor(0, 0);
|
||||||
ExPolygons result;
|
ExPolygons result;
|
||||||
const FontFile& font = *font_with_cache.font_file;
|
const FontFile& font = *font_with_cache.font_file;
|
||||||
unsigned int font_index = font_prop.collection_number.has_value()?
|
unsigned int font_index = font_prop.collection_number.value_or(0);
|
||||||
*font_prop.collection_number : 0;
|
|
||||||
if (!priv::is_valid(font, font_index)) return {};
|
if (!priv::is_valid(font, font_index)) return {};
|
||||||
const FontFile::Info& info = font.infos[font_index];
|
const FontFile::Info& info = font.infos[font_index];
|
||||||
Glyphs& cache = *font_with_cache.cache;
|
Glyphs& cache = *font_with_cache.cache;
|
||||||
|
@ -36,6 +36,7 @@ namespace pt = boost::property_tree;
|
|||||||
#include "miniz_extension.hpp"
|
#include "miniz_extension.hpp"
|
||||||
|
|
||||||
#include "TextConfiguration.hpp"
|
#include "TextConfiguration.hpp"
|
||||||
|
#include "EmbossShape.hpp"
|
||||||
|
|
||||||
#include <fast_float/fast_float.h>
|
#include <fast_float/fast_float.h>
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ template<typename T> struct Limit {
|
|||||||
// Variable keep limits for variables
|
// Variable keep limits for variables
|
||||||
static const struct Limits
|
static const struct Limits
|
||||||
{
|
{
|
||||||
MinMax<float> emboss{0.01f, 1e4f}; // in mm
|
MinMax<double> emboss{0.01, 1e4}; // in mm
|
||||||
MinMax<float> size_in_mm{0.1f, 1000.f}; // in mm
|
MinMax<float> size_in_mm{0.1f, 1000.f}; // in mm
|
||||||
Limit<float> boldness{{-200.f, 200.f}, {-2e4f, 2e4f}}; // in font points
|
Limit<float> boldness{{-200.f, 200.f}, {-2e4f, 2e4f}}; // in font points
|
||||||
Limit<float> skew{{-1.f, 1.f}, {-100.f, 100.f}}; // ration without unit
|
Limit<float> skew{{-1.f, 1.f}, {-100.f, 100.f}}; // ration without unit
|
||||||
@ -91,9 +91,6 @@ static const struct Limits
|
|||||||
MinMax<int> line_gap{-20000, 20000}; // in font points
|
MinMax<int> line_gap{-20000, 20000}; // in font points
|
||||||
// distance text object from surface
|
// distance text object from surface
|
||||||
MinMax<float> angle{-180.f, 180.f}; // in degrees
|
MinMax<float> angle{-180.f, 180.f}; // in degrees
|
||||||
|
|
||||||
// Distance of text volume from surface to be represented as distance surface
|
|
||||||
MinMax<double> calc_distance_sq{1e-4, 10.}; // in mm
|
|
||||||
} limits;
|
} limits;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -117,16 +114,17 @@ ImVec2 calc_fine_position(const Selection &selection, const ImVec2 &windows_size
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
struct TextDataBase : public DataBase
|
struct TextDataBase : public DataBase
|
||||||
{
|
{
|
||||||
TextDataBase(DataBase &&parent, const FontFileWithCache &font_file, TextConfiguration &&text_configuration);
|
TextDataBase(DataBase &&parent, const FontFileWithCache &font_file,
|
||||||
|
TextConfiguration &&text_configuration, const EmbossProjection& projection);
|
||||||
// Create shape from text + font configuration
|
// Create shape from text + font configuration
|
||||||
EmbossShape &create_shape() override;
|
EmbossShape &create_shape() override;
|
||||||
void write(ModelVolume &volume) const override;
|
void write(ModelVolume &volume) const override;
|
||||||
|
|
||||||
// private:
|
private:
|
||||||
// Keep pointer on Data of font (glyph shapes)
|
// Keep pointer on Data of font (glyph shapes)
|
||||||
FontFileWithCache font_file;
|
FontFileWithCache m_font_file;
|
||||||
// font item is not used for create object
|
// font item is not used for create object
|
||||||
TextConfiguration text_configuration;
|
TextConfiguration m_text_configuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Loaded icons enum
|
// Loaded icons enum
|
||||||
@ -270,6 +268,7 @@ struct GuiCfg
|
|||||||
};
|
};
|
||||||
Translations translations;
|
Translations translations;
|
||||||
};
|
};
|
||||||
|
GuiCfg create_gui_configuration();
|
||||||
|
|
||||||
void draw_font_preview(FaceName &face, const std::string &text, Facenames &faces, const GuiCfg &cfg, bool is_visible);
|
void draw_font_preview(FaceName &face, const std::string &text, Facenames &faces, const GuiCfg &cfg, bool is_visible);
|
||||||
} // namespace priv
|
} // namespace priv
|
||||||
@ -542,11 +541,12 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port
|
m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port
|
||||||
) {
|
) {
|
||||||
// Create cache for gui offsets
|
// Create cache for gui offsets
|
||||||
GuiCfg cfg = create_gui_configuration();
|
::GuiCfg cfg = create_gui_configuration();
|
||||||
cfg.screen_scale = screen_scale;
|
cfg.screen_scale = screen_scale;
|
||||||
cfg.main_toolbar_height = main_toolbar_height;
|
cfg.main_toolbar_height = main_toolbar_height;
|
||||||
|
|
||||||
m_gui_cfg = std::make_unique<const GuiCfg>(std::move(cfg));
|
GuiCfg gui_cfg{std::move(cfg)};
|
||||||
|
m_gui_cfg = std::make_unique<const GuiCfg>(std::move(gui_cfg));
|
||||||
// set position near toolbar
|
// set position near toolbar
|
||||||
m_set_window_offset = ImVec2(-1.f, -1.f);
|
m_set_window_offset = ImVec2(-1.f, -1.f);
|
||||||
|
|
||||||
@ -711,102 +711,6 @@ void GLGizmoEmboss::on_stop_dragging()
|
|||||||
}
|
}
|
||||||
void GLGizmoEmboss::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); }
|
void GLGizmoEmboss::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); }
|
||||||
|
|
||||||
GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration()
|
|
||||||
{
|
|
||||||
GuiCfg cfg; // initialize by default values;
|
|
||||||
|
|
||||||
float line_height = ImGui::GetTextLineHeight();
|
|
||||||
float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing();
|
|
||||||
float space = line_height_with_spacing - line_height;
|
|
||||||
const ImGuiStyle &style = ImGui::GetStyle();
|
|
||||||
|
|
||||||
cfg.max_style_name_width = ImGui::CalcTextSize("Maximal font name, extended").x;
|
|
||||||
|
|
||||||
cfg.icon_width = static_cast<unsigned int>(std::ceil(line_height));
|
|
||||||
// make size pair number
|
|
||||||
if (cfg.icon_width % 2 != 0) ++cfg.icon_width;
|
|
||||||
|
|
||||||
cfg.delete_pos_x = cfg.max_style_name_width + space;
|
|
||||||
const float count_line_of_text = 3.f;
|
|
||||||
cfg.text_size = ImVec2(-FLT_MIN, line_height_with_spacing * count_line_of_text);
|
|
||||||
ImVec2 letter_m_size = ImGui::CalcTextSize("M");
|
|
||||||
const float count_letter_M_in_input = 12.f;
|
|
||||||
cfg.input_width = letter_m_size.x * count_letter_M_in_input;
|
|
||||||
GuiCfg::Translations &tr = cfg.translations;
|
|
||||||
|
|
||||||
tr.font = _u8L("Font");
|
|
||||||
tr.height = _u8L("Height");
|
|
||||||
tr.depth = _u8L("Depth");
|
|
||||||
|
|
||||||
float max_text_width = std::max({
|
|
||||||
ImGui::CalcTextSize(tr.font.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.height.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.depth.c_str()).x});
|
|
||||||
cfg.indent = static_cast<float>(cfg.icon_width);
|
|
||||||
cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space;
|
|
||||||
|
|
||||||
tr.use_surface = _u8L("Use surface");
|
|
||||||
tr.char_gap = _u8L("Char gap");
|
|
||||||
tr.line_gap = _u8L("Line gap");
|
|
||||||
tr.boldness = _u8L("Boldness");
|
|
||||||
tr.skew_ration = _u8L("Skew ratio");
|
|
||||||
tr.from_surface = _u8L("From surface");
|
|
||||||
tr.rotation = _u8L("Rotation");
|
|
||||||
tr.keep_up = _u8L("Keep Up");
|
|
||||||
tr.collection = _u8L("Collection");
|
|
||||||
|
|
||||||
float max_advanced_text_width = std::max({
|
|
||||||
ImGui::CalcTextSize(tr.use_surface.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.char_gap.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.line_gap.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.boldness.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.skew_ration.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.from_surface.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.rotation.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.keep_up.c_str()).x,
|
|
||||||
ImGui::CalcTextSize(tr.collection.c_str()).x });
|
|
||||||
cfg.advanced_input_offset = max_advanced_text_width
|
|
||||||
+ 3 * space + cfg.indent;
|
|
||||||
|
|
||||||
// calculate window size
|
|
||||||
float window_title = line_height + 2*style.FramePadding.y + 2 * style.WindowTitleAlign.y;
|
|
||||||
float input_height = line_height_with_spacing + 2*style.FramePadding.y;
|
|
||||||
float tree_header = line_height_with_spacing;
|
|
||||||
float separator_height = 1 + style.FramePadding.y;
|
|
||||||
|
|
||||||
// "Text is to object" + radio buttons
|
|
||||||
cfg.height_of_volume_type_selector = separator_height + line_height_with_spacing + input_height;
|
|
||||||
|
|
||||||
float window_height =
|
|
||||||
window_title + // window title
|
|
||||||
cfg.text_size.y + // text field
|
|
||||||
input_height * 4 + // font name + height + depth + style selector
|
|
||||||
tree_header + // advance tree
|
|
||||||
separator_height + // presets separator line
|
|
||||||
line_height_with_spacing + // "Presets"
|
|
||||||
2 * style.WindowPadding.y;
|
|
||||||
float window_width = cfg.input_offset + cfg.input_width + 2*style.WindowPadding.x
|
|
||||||
+ 2 * (cfg.icon_width + space);
|
|
||||||
cfg.minimal_window_size = ImVec2(window_width, window_height);
|
|
||||||
|
|
||||||
// 9 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, keepUp, textFaceToCamera
|
|
||||||
// 4 = 1px for fix each edit image of drag float
|
|
||||||
float advance_height = input_height * 9 + 8;
|
|
||||||
cfg.minimal_window_size_with_advance =
|
|
||||||
ImVec2(cfg.minimal_window_size.x,
|
|
||||||
cfg.minimal_window_size.y + advance_height);
|
|
||||||
|
|
||||||
cfg.minimal_window_size_with_collections =
|
|
||||||
ImVec2(cfg.minimal_window_size_with_advance.x,
|
|
||||||
cfg.minimal_window_size_with_advance.y + input_height);
|
|
||||||
|
|
||||||
int max_style_image_width = static_cast<int>(std::round(cfg.max_style_name_width/2 - 2 * style.FramePadding.x));
|
|
||||||
int max_style_image_height = static_cast<int>(std::round(1.5 * input_height));
|
|
||||||
cfg.max_style_image_size = Vec2i(max_style_image_width, max_style_image_height);
|
|
||||||
cfg.face_name_size.y() = line_height_with_spacing;
|
|
||||||
return cfg;
|
|
||||||
}
|
|
||||||
|
|
||||||
EmbossStyles GLGizmoEmboss::create_default_styles()
|
EmbossStyles GLGizmoEmboss::create_default_styles()
|
||||||
{
|
{
|
||||||
wxFontEnumerator::InvalidateCache();
|
wxFontEnumerator::InvalidateCache();
|
||||||
@ -881,66 +785,48 @@ void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); }
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Throow ray by embossing params to object and find surface point
|
/// Check installed fonts whether optional face name exist in installed fonts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="gl_volume">Define embossed volume</param>
|
/// <param name="face_name_opt">Name from style - style.prop.face_name</param>
|
||||||
/// <param name="raycaster">Way to cast rays to object</param>
|
/// <param name="face_names">All installed good and bad fonts - not const must be possible to initialize it</param>
|
||||||
/// <param name="canvas">Contain model</param>
|
/// <returns>When it could be installed it contain value(potentionaly empty string)</returns>
|
||||||
/// <returns>Calculated distance from surface</returns>
|
std::optional<wxString> get_installed_face_name(const std::optional<std::string> &face_name_opt, ::Facenames& face_names)
|
||||||
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);
|
// Could exist OS without getter on face_name,
|
||||||
assert(object != nullptr);
|
// but it is able to restore font from descriptor
|
||||||
if (object == nullptr)
|
// Soo default value must be TRUE
|
||||||
return {};
|
if (face_name_opt.has_value())
|
||||||
|
return wxString();
|
||||||
|
|
||||||
const ModelInstance *instance = get_model_instance(gl_volume, *object);
|
wxString face_name(face_name_opt->c_str());
|
||||||
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())
|
// search in enumerated fonts
|
||||||
return {};
|
// refresh list of installed font in the OS.
|
||||||
|
init_face_names(face_names);
|
||||||
|
face_names.is_init = false;
|
||||||
|
|
||||||
const ModelVolumePtrs &volumes = object->volumes;
|
auto cmp = [](const FaceName &fn, const wxString &wx_name) { return fn.wx_name < wx_name; };
|
||||||
std::vector<size_t> allowed_volumes_id;
|
const std::vector<FaceName> &faces = face_names.faces;
|
||||||
allowed_volumes_id.reserve(volumes.size() - 1);
|
// is font installed?
|
||||||
for (const ModelVolume *v : volumes) {
|
if (auto it = std::lower_bound(faces.begin(), faces.end(), face_name, cmp);
|
||||||
// skip actual selected object
|
it != faces.end() && it->wx_name == face_name)
|
||||||
if (v->id() == volume->id())
|
return face_name;
|
||||||
continue;
|
|
||||||
// collect hit only from object parts not modifiers neither negative
|
const std::vector<wxString> &bad = face_names.bad;
|
||||||
if (!v->is_model_part())
|
auto it_bad = std::lower_bound(bad.begin(), bad.end(), face_name);
|
||||||
continue;
|
if (it_bad == bad.end() || *it_bad != face_name) {
|
||||||
allowed_volumes_id.emplace_back(v->id().id);
|
// 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?
|
||||||
|
) {
|
||||||
|
return wxString();
|
||||||
|
// QUESTION: add this name to allowed faces?
|
||||||
|
// Could create twin of font face name
|
||||||
|
// When not add it will be hard to select it again when change font
|
||||||
}
|
}
|
||||||
RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id));
|
}
|
||||||
RaycastManager::Meshes meshes = create_meshes(canvas, condition);
|
return {}; // not installed
|
||||||
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.closest_hit(p, dir, &condition);
|
|
||||||
if (!hit_opt.has_value())
|
|
||||||
return {};
|
|
||||||
const RaycastManager::Hit &hit = *hit_opt;
|
|
||||||
|
|
||||||
// too small distance is calculated as zero distance
|
|
||||||
if (hit.squared_distance < limits.calc_distance_sq.min)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
// check macimal distance
|
|
||||||
double max_squared_distance = std::max(std::pow(2 * volume->emboss_shape->projection.depth, 2), limits.calc_distance_sq.max);
|
|
||||||
if (hit.squared_distance > max_squared_distance)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
// calculate sign
|
|
||||||
float sign = ((hit.position - p).dot(dir) > 0)? 1.f : -1.f;
|
|
||||||
|
|
||||||
// distiguish sign
|
|
||||||
return sign * static_cast<float>(sqrt(hit.squared_distance));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -981,46 +867,11 @@ void GLGizmoEmboss::set_volume_by_selection()
|
|||||||
const TextConfiguration &tc = *tc_opt;
|
const TextConfiguration &tc = *tc_opt;
|
||||||
const EmbossStyle &style = tc.style;
|
const EmbossStyle &style = tc.style;
|
||||||
|
|
||||||
// Could exist OS without getter on face_name,
|
std::optional<wxString> installed_name = get_installed_face_name(style.prop.face_name, *m_face_names);
|
||||||
// but it is able to restore font from descriptor
|
|
||||||
// Soo default value must be TRUE
|
|
||||||
bool is_font_installed = true;
|
|
||||||
wxString face_name;
|
|
||||||
if (const std::optional<std::string> &face_name_opt = style.prop.face_name;
|
|
||||||
face_name_opt.has_value()) {
|
|
||||||
face_name = wxString(face_name_opt->c_str());
|
|
||||||
|
|
||||||
// search in enumerated fonts
|
|
||||||
// refresh list of installed font in the OS.
|
|
||||||
init_face_names(*m_face_names);
|
|
||||||
m_face_names->is_init = false;
|
|
||||||
|
|
||||||
auto cmp = [](const ::FaceName &fn, const wxString &wx_name) { return fn.wx_name < wx_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) {
|
|
||||||
// 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?
|
|
||||||
) {
|
|
||||||
is_font_installed = true;
|
|
||||||
// QUESTION: add this name to allowed faces?
|
|
||||||
// Could create twin of font face name
|
|
||||||
// When not add it will be hard to select it again when change font
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wxFont wx_font;
|
wxFont wx_font;
|
||||||
// load wxFont from same OS when font name is installed
|
// load wxFont from same OS when font name is installed
|
||||||
if (style.type == WxFontUtils::get_current_type() && is_font_installed)
|
if (style.type == WxFontUtils::get_current_type() && installed_name.has_value())
|
||||||
wx_font = WxFontUtils::load_wxFont(style.path);
|
wx_font = WxFontUtils::load_wxFont(style.path);
|
||||||
|
|
||||||
// Flag that is selected same font
|
// Flag that is selected same font
|
||||||
@ -1030,8 +881,8 @@ void GLGizmoEmboss::set_volume_by_selection()
|
|||||||
is_exact_font = false;
|
is_exact_font = false;
|
||||||
// Try create similar wx font by FontFamily
|
// Try create similar wx font by FontFamily
|
||||||
wx_font = WxFontUtils::create_wxFont(style);
|
wx_font = WxFontUtils::create_wxFont(style);
|
||||||
if (is_font_installed && !face_name.empty())
|
if (installed_name.has_value() && !installed_name->empty())
|
||||||
is_exact_font = wx_font.SetFaceName(face_name);
|
is_exact_font = wx_font.SetFaceName(*installed_name);
|
||||||
|
|
||||||
// Have to use some wxFont
|
// Have to use some wxFont
|
||||||
if (!wx_font.IsOk())
|
if (!wx_font.IsOk())
|
||||||
@ -1163,16 +1014,17 @@ 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{create_emboss_data_base(m_text, m_style_manager, m_job_cancel), m_volume->id()};
|
assert(m_volume->text_configuration.has_value());
|
||||||
|
if (!m_volume->text_configuration.has_value()) return false;
|
||||||
|
assert(m_volume->emboss_shape.has_value());
|
||||||
|
if (!m_volume->emboss_shape.has_value()) return false;
|
||||||
|
|
||||||
|
DataUpdate data{create_emboss_data_base(m_text, m_style_manager, m_job_cancel), m_volume->id()};
|
||||||
std::unique_ptr<Job> job = nullptr;
|
std::unique_ptr<Job> job = nullptr;
|
||||||
|
|
||||||
// check cutting from source mesh
|
// check cutting from source mesh
|
||||||
// TODO: do it better way
|
bool &use_surface = data.base->shape.projection.use_surface;
|
||||||
FontProp &fp = static_cast<TextDataBase *>(data.base.get())->text_configuration.style.prop;
|
if (use_surface && m_volume->is_the_only_one_part())
|
||||||
bool &use_surface = fp.use_surface;
|
|
||||||
bool is_object = m_volume->get_object()->volumes.size() == 1;
|
|
||||||
if (use_surface && is_object)
|
|
||||||
use_surface = false;
|
use_surface = false;
|
||||||
|
|
||||||
if (use_surface) {
|
if (use_surface) {
|
||||||
@ -1181,12 +1033,12 @@ bool GLGizmoEmboss::process()
|
|||||||
if (sources.empty()) return false;
|
if (sources.empty()) return false;
|
||||||
|
|
||||||
Transform3d text_tr = m_volume->get_matrix();
|
Transform3d text_tr = m_volume->get_matrix();
|
||||||
auto& fix_3mf = m_volume->text_configuration->fix_3mf_tr;
|
auto& fix_3mf = m_volume->emboss_shape->fix_3mf_tr;
|
||||||
if (fix_3mf.has_value())
|
if (fix_3mf.has_value())
|
||||||
text_tr = text_tr * fix_3mf->inverse();
|
text_tr = text_tr * fix_3mf->inverse();
|
||||||
|
|
||||||
// when it is new applying of use surface than move origin onto surfaca
|
// when it is new applying of use surface than move origin onto surfaca
|
||||||
if (!m_volume->text_configuration->style.prop.use_surface) {
|
if (!m_volume->emboss_shape->projection.use_surface) {
|
||||||
auto offset = calc_surface_offset(m_parent.get_selection(), m_raycast_manager);
|
auto offset = calc_surface_offset(m_parent.get_selection(), m_raycast_manager);
|
||||||
if (offset.has_value())
|
if (offset.has_value())
|
||||||
text_tr *= Eigen::Translation<double, 3>(*offset);
|
text_tr *= Eigen::Translation<double, 3>(*offset);
|
||||||
@ -1194,8 +1046,7 @@ bool GLGizmoEmboss::process()
|
|||||||
|
|
||||||
bool is_outside = m_volume->is_model_part();
|
bool is_outside = m_volume->is_model_part();
|
||||||
// check that there is not unexpected volume type
|
// check that there is not unexpected volume type
|
||||||
assert(is_outside || m_volume->is_negative_volume() ||
|
assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier());
|
||||||
m_volume->is_modifier());
|
|
||||||
UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, is_outside, std::move(sources)}};
|
UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, is_outside, std::move(sources)}};
|
||||||
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
|
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
|
||||||
} else {
|
} else {
|
||||||
@ -1215,8 +1066,8 @@ bool GLGizmoEmboss::process()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace priv {
|
namespace {
|
||||||
static bool is_text_empty(const std::string &text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; }
|
bool is_text_empty(std::string_view text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; }
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoEmboss::close()
|
void GLGizmoEmboss::close()
|
||||||
@ -1224,7 +1075,7 @@ void GLGizmoEmboss::close()
|
|||||||
// remove volume when text is empty
|
// remove volume when text is empty
|
||||||
if (m_volume != nullptr &&
|
if (m_volume != nullptr &&
|
||||||
m_volume->text_configuration.has_value() &&
|
m_volume->text_configuration.has_value() &&
|
||||||
priv::is_text_empty(m_text)) {
|
is_text_empty(m_text)) {
|
||||||
Plater &p = *wxGetApp().plater();
|
Plater &p = *wxGetApp().plater();
|
||||||
// is the text object?
|
// is the text object?
|
||||||
if (m_volume->is_the_only_one_part()) {
|
if (m_volume->is_the_only_one_part()) {
|
||||||
@ -1381,7 +1232,7 @@ void GLGizmoEmboss::draw_text_input()
|
|||||||
warning_tool_tip += "\n";
|
warning_tool_tip += "\n";
|
||||||
warning_tool_tip += t;
|
warning_tool_tip += t;
|
||||||
};
|
};
|
||||||
if (priv::is_text_empty(m_text))
|
if (is_text_empty(m_text))
|
||||||
append_warning(_u8L("Embossed text can NOT contain only white spaces."));
|
append_warning(_u8L("Embossed text can NOT contain only white spaces."));
|
||||||
if (m_text_contain_unknown_glyph)
|
if (m_text_contain_unknown_glyph)
|
||||||
append_warning(_u8L("Text contain character glyph (represented by '?') unknown by font."));
|
append_warning(_u8L("Text contain character glyph (represented by '?') unknown by font."));
|
||||||
@ -1417,14 +1268,12 @@ void GLGizmoEmboss::draw_text_input()
|
|||||||
// warning tooltip has to be with default font
|
// warning tooltip has to be with default font
|
||||||
if (!warning_tool_tip.empty()) {
|
if (!warning_tool_tip.empty()) {
|
||||||
// Multiline input has hidden window for scrolling
|
// Multiline input has hidden window for scrolling
|
||||||
ImGuiWindow *input = ImGui::GetCurrentWindow()->DC.ChildWindows.front();
|
const ImGuiWindow *input = ImGui::GetCurrentWindow()->DC.ChildWindows.front();
|
||||||
|
|
||||||
const ImGuiStyle &style = ImGui::GetStyle();
|
const ImGuiStyle &style = ImGui::GetStyle();
|
||||||
float scrollbar_width = (input->ScrollbarY) ? style.ScrollbarSize : 0.f;
|
float scrollbar_width = (input->ScrollbarY) ? style.ScrollbarSize : 0.f;
|
||||||
float scrollbar_height = (input->ScrollbarX) ? style.ScrollbarSize : 0.f;
|
float scrollbar_height = (input->ScrollbarX) ? style.ScrollbarSize : 0.f;
|
||||||
|
|
||||||
bool hovered = ImGui::IsItemHovered();
|
if (ImGui::IsItemHovered())
|
||||||
if (hovered)
|
|
||||||
ImGui::SetTooltip("%s", warning_tool_tip.c_str());
|
ImGui::SetTooltip("%s", warning_tool_tip.c_str());
|
||||||
|
|
||||||
ImVec2 cursor = ImGui::GetCursorPos();
|
ImVec2 cursor = ImGui::GetCursorPos();
|
||||||
@ -1443,7 +1292,7 @@ void GLGizmoEmboss::draw_text_input()
|
|||||||
// IMPROVE: only extend not clear
|
// IMPROVE: only extend not clear
|
||||||
// Extend font ranges
|
// Extend font ranges
|
||||||
if (!range_text.empty() &&
|
if (!range_text.empty() &&
|
||||||
!m_imgui->contain_all_glyphs(imgui_font, range_text) )
|
!ImGuiWrapper::contain_all_glyphs(imgui_font, range_text) )
|
||||||
m_style_manager.clear_imgui_font();
|
m_style_manager.clear_imgui_font();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1471,7 +1320,7 @@ void GLGizmoEmboss::init_font_name_texture() {
|
|||||||
glsafe(::glBindTexture(target, no_texture_id));
|
glsafe(::glBindTexture(target, no_texture_id));
|
||||||
|
|
||||||
// clear info about creation of texture - no one is initialized yet
|
// clear info about creation of texture - no one is initialized yet
|
||||||
for (::FaceName &face : m_face_names->faces) {
|
for (FaceName &face : m_face_names->faces) {
|
||||||
face.cancel = nullptr;
|
face.cancel = nullptr;
|
||||||
face.is_created = nullptr;
|
face.is_created = nullptr;
|
||||||
}
|
}
|
||||||
@ -1484,8 +1333,7 @@ bool GLGizmoEmboss::select_facename(const wxString &facename)
|
|||||||
{
|
{
|
||||||
if (!wxFontEnumerator::IsValidFacename(facename)) return false;
|
if (!wxFontEnumerator::IsValidFacename(facename)) return false;
|
||||||
// Select font
|
// Select font
|
||||||
const wxFontEncoding &encoding = m_face_names->encoding;
|
wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(Facenames::encoding));
|
||||||
wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(encoding));
|
|
||||||
if (!wx_font.IsOk()) return false;
|
if (!wx_font.IsOk()) return false;
|
||||||
#ifdef USE_PIXEL_SIZE_IN_WX_FONT
|
#ifdef USE_PIXEL_SIZE_IN_WX_FONT
|
||||||
// wx font could change source file by size of font
|
// wx font could change source file by size of font
|
||||||
@ -1590,7 +1438,7 @@ void GLGizmoEmboss::draw_font_list()
|
|||||||
std::string search = m_face_names->search; // copy
|
std::string search = m_face_names->search; // copy
|
||||||
std::transform(search.begin(), search.end(), search.begin(), ::toupper);
|
std::transform(search.begin(), search.end(), search.begin(), ::toupper);
|
||||||
|
|
||||||
for (::FaceName &face : m_face_names->faces) {
|
for (const FaceName &face : m_face_names->faces) {
|
||||||
size_t index = &face - &m_face_names->faces.front();
|
size_t index = &face - &m_face_names->faces.front();
|
||||||
|
|
||||||
// font name to uppercase
|
// font name to uppercase
|
||||||
@ -1629,7 +1477,7 @@ void GLGizmoEmboss::draw_font_list()
|
|||||||
if (m_face_names->texture_id == 0)
|
if (m_face_names->texture_id == 0)
|
||||||
init_font_name_texture();
|
init_font_name_texture();
|
||||||
|
|
||||||
for (::FaceName &face : m_face_names->faces) {
|
for (FaceName &face : m_face_names->faces) {
|
||||||
const wxString &wx_face_name = face.wx_name;
|
const wxString &wx_face_name = face.wx_name;
|
||||||
size_t index = &face - &m_face_names->faces.front();
|
size_t index = &face - &m_face_names->faces.front();
|
||||||
|
|
||||||
@ -1670,7 +1518,7 @@ void GLGizmoEmboss::draw_font_list()
|
|||||||
m_face_names->is_init = false;
|
m_face_names->is_init = false;
|
||||||
m_face_names->hide.clear();
|
m_face_names->hide.clear();
|
||||||
// cancel all process for generation of texture
|
// cancel all process for generation of texture
|
||||||
for (::FaceName &face : m_face_names->faces)
|
for (FaceName &face : m_face_names->faces)
|
||||||
if (face.cancel != nullptr)
|
if (face.cancel != nullptr)
|
||||||
face.cancel->store(true);
|
face.cancel->store(true);
|
||||||
glsafe(::glDeleteTextures(1, &m_face_names->texture_id));
|
glsafe(::glDeleteTextures(1, &m_face_names->texture_id));
|
||||||
@ -1769,7 +1617,7 @@ void GLGizmoEmboss::draw_model_type()
|
|||||||
m_volume->set_type(*new_type);
|
m_volume->set_type(*new_type);
|
||||||
|
|
||||||
// Update volume position when switch from part or into part
|
// Update volume position when switch from part or into part
|
||||||
if (m_volume->text_configuration->style.prop.use_surface) {
|
if (m_volume->emboss_shape->projection.use_surface) {
|
||||||
// move inside
|
// move inside
|
||||||
bool is_volume_move_inside = (type == part);
|
bool is_volume_move_inside = (type == part);
|
||||||
bool is_volume_move_outside = (*new_type == part);
|
bool is_volume_move_outside = (*new_type == part);
|
||||||
@ -1804,9 +1652,9 @@ void GLGizmoEmboss::draw_style_rename_popup() {
|
|||||||
|
|
||||||
bool allow_change = false;
|
bool allow_change = false;
|
||||||
if (new_name.empty()) {
|
if (new_name.empty()) {
|
||||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name can't be empty."));
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name can't be empty."));
|
||||||
}else if (!is_unique) {
|
}else if (!is_unique) {
|
||||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name has to be unique."));
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name has to be unique."));
|
||||||
} else {
|
} else {
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
allow_change = true;
|
allow_change = true;
|
||||||
@ -1824,7 +1672,7 @@ void GLGizmoEmboss::draw_style_rename_popup() {
|
|||||||
|
|
||||||
if (store) {
|
if (store) {
|
||||||
// rename style in all objects and volumes
|
// rename style in all objects and volumes
|
||||||
for (ModelObject *mo :wxGetApp().plater()->model().objects) {
|
for (const ModelObject *mo :wxGetApp().plater()->model().objects) {
|
||||||
for (ModelVolume *mv : mo->volumes) {
|
for (ModelVolume *mv : mo->volumes) {
|
||||||
if (!mv->text_configuration.has_value()) continue;
|
if (!mv->text_configuration.has_value()) continue;
|
||||||
std::string& name = mv->text_configuration->style.name;
|
std::string& name = mv->text_configuration->style.name;
|
||||||
@ -1885,9 +1733,9 @@ void GLGizmoEmboss::draw_style_save_as_popup() {
|
|||||||
bool is_unique = m_style_manager.is_unique_style_name(new_name);
|
bool is_unique = m_style_manager.is_unique_style_name(new_name);
|
||||||
bool allow_change = false;
|
bool allow_change = false;
|
||||||
if (new_name.empty()) {
|
if (new_name.empty()) {
|
||||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name can't be empty."));
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name can't be empty."));
|
||||||
}else if (!is_unique) {
|
}else if (!is_unique) {
|
||||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name has to be unique."));
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name has to be unique."));
|
||||||
} else {
|
} else {
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
allow_change = true;
|
allow_change = true;
|
||||||
@ -1943,7 +1791,7 @@ void GLGizmoEmboss::draw_style_add_button()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal(popup_id, 0, ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal(popup_id, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
m_imgui->disable_background_fadeout_animation();
|
m_imgui->disable_background_fadeout_animation();
|
||||||
draw_style_save_as_popup();
|
draw_style_save_as_popup();
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
@ -2056,11 +1904,11 @@ void GLGizmoEmboss::draw_style_list() {
|
|||||||
trunc_name = ImGuiWrapper::trunc(current_name, max_style_name_width);
|
trunc_name = ImGuiWrapper::trunc(current_name, max_style_name_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string title = _u8L("Presets");
|
std::string presets = _u8L("Presets");
|
||||||
if (m_style_manager.exist_stored_style())
|
if (m_style_manager.exist_stored_style())
|
||||||
ImGui::Text("%s", title.c_str());
|
ImGui::Text("%s", presets.c_str());
|
||||||
else
|
else
|
||||||
ImGui::TextColored(ImGuiWrapper::COL_ORANGE_LIGHT, "%s", title.c_str());
|
ImGui::TextColored(ImGuiWrapper::COL_ORANGE_LIGHT, "%s", presets.c_str());
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
|
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
|
||||||
auto add_text_modify = [&is_modified](const std::string& name) {
|
auto add_text_modify = [&is_modified](const std::string& name) {
|
||||||
@ -2281,7 +2129,7 @@ bool GLGizmoEmboss::revertible(const std::string &name,
|
|||||||
const T *default_value,
|
const T *default_value,
|
||||||
const std::string &undo_tooltip,
|
const std::string &undo_tooltip,
|
||||||
float undo_offset,
|
float undo_offset,
|
||||||
Draw draw)
|
Draw draw) const
|
||||||
{
|
{
|
||||||
bool changed = exist_change(value, default_value);
|
bool changed = exist_change(value, default_value);
|
||||||
if (changed || default_value == nullptr)
|
if (changed || default_value == nullptr)
|
||||||
@ -2301,43 +2149,43 @@ bool GLGizmoEmboss::revertible(const std::string &name,
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
// May be move to ImGuiWrapper
|
||||||
|
template<typename T> bool imgui_input(const char *label, T *v, T step, T step_fast, const char *format, ImGuiInputTextFlags flags);
|
||||||
|
template<> bool imgui_input(const char *label, float *v, float step, float step_fast, const char *format, ImGuiInputTextFlags flags)
|
||||||
|
{ return ImGui::InputFloat(label, v, step, step_fast, format, flags); }
|
||||||
|
template<> bool imgui_input(const char *label, double *v, double step, double step_fast, const char *format, ImGuiInputTextFlags flags)
|
||||||
|
{ return ImGui::InputDouble(label, v, step, step_fast, format, flags); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
bool GLGizmoEmboss::rev_input(const std::string &name,
|
bool GLGizmoEmboss::rev_input(const std::string &name, T &value, const T *default_value,
|
||||||
float &value,
|
const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags) const
|
||||||
const float *default_value,
|
|
||||||
const std::string &undo_tooltip,
|
|
||||||
float step,
|
|
||||||
float step_fast,
|
|
||||||
const char *format,
|
|
||||||
ImGuiInputTextFlags flags)
|
|
||||||
{
|
{
|
||||||
// draw offseted input
|
// draw offseted input
|
||||||
auto draw_offseted_input = [&]()->bool{
|
auto draw_offseted_input = [&offset = m_gui_cfg->input_offset, &width = m_gui_cfg->input_width,
|
||||||
float input_offset = m_gui_cfg->input_offset;
|
&name, &value, &step, &step_fast, format, flags](){
|
||||||
float input_width = m_gui_cfg->input_width;
|
ImGui::SameLine(offset);
|
||||||
ImGui::SameLine(input_offset);
|
ImGui::SetNextItemWidth(width);
|
||||||
ImGui::SetNextItemWidth(input_width);
|
return imgui_input(("##" + name).c_str(),
|
||||||
return ImGui::InputFloat(("##" + name).c_str(),
|
|
||||||
&value, step, step_fast, format, flags);
|
&value, step, step_fast, format, flags);
|
||||||
};
|
};
|
||||||
float undo_offset = ImGui::GetStyle().FramePadding.x;
|
float undo_offset = ImGui::GetStyle().FramePadding.x;
|
||||||
return revertible(name, value, default_value, undo_tooltip, undo_offset, draw_offseted_input);
|
return revertible(name, value, default_value, undo_tooltip, undo_offset, draw_offseted_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
bool GLGizmoEmboss::rev_input_mm(const std::string &name,
|
bool GLGizmoEmboss::rev_input_mm(const std::string &name,
|
||||||
float &value,
|
T &value,
|
||||||
const float *default_value_ptr,
|
const T *default_value_ptr,
|
||||||
const std::string &undo_tooltip,
|
const std::string &undo_tooltip,
|
||||||
float step,
|
T step,
|
||||||
float step_fast,
|
T step_fast,
|
||||||
const char *format,
|
const char *format,
|
||||||
bool use_inch,
|
bool use_inch,
|
||||||
const std::optional<float>& scale)
|
const std::optional<float> &scale) const
|
||||||
{
|
{
|
||||||
// _variable which temporary keep value
|
// _variable which temporary keep value
|
||||||
float value_ = value;
|
T value_ = value;
|
||||||
float default_value_;
|
T default_value_;
|
||||||
if (use_inch) {
|
if (use_inch) {
|
||||||
// calc value in inch
|
// calc value in inch
|
||||||
value_ *= ObjectManipulation::mm_to_in;
|
value_ *= ObjectManipulation::mm_to_in;
|
||||||
@ -2365,11 +2213,11 @@ bool GLGizmoEmboss::rev_input_mm(const std::string &name,
|
|||||||
bool GLGizmoEmboss::rev_checkbox(const std::string &name,
|
bool GLGizmoEmboss::rev_checkbox(const std::string &name,
|
||||||
bool &value,
|
bool &value,
|
||||||
const bool *default_value,
|
const bool *default_value,
|
||||||
const std::string &undo_tooltip)
|
const std::string &undo_tooltip) const
|
||||||
{
|
{
|
||||||
// draw offseted input
|
// draw offseted input
|
||||||
auto draw_offseted_input = [&]() -> bool {
|
auto draw_offseted_input = [&offset = m_gui_cfg->advanced_input_offset, &name, &value](){
|
||||||
ImGui::SameLine(m_gui_cfg->advanced_input_offset);
|
ImGui::SameLine(offset);
|
||||||
return ImGui::Checkbox(("##" + name).c_str(), &value);
|
return ImGui::Checkbox(("##" + name).c_str(), &value);
|
||||||
};
|
};
|
||||||
float undo_offset = ImGui::GetStyle().FramePadding.x;
|
float undo_offset = ImGui::GetStyle().FramePadding.x;
|
||||||
@ -2417,29 +2265,23 @@ void GLGizmoEmboss::draw_height(bool use_inch)
|
|||||||
process();
|
process();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLGizmoEmboss::set_depth()
|
void GLGizmoEmboss::draw_depth(bool use_inch)
|
||||||
{
|
{
|
||||||
float &value = m_style_manager.get_style().prop.emboss;
|
double &value = m_style_manager.get_style().projection.depth;
|
||||||
|
const StyleManager::Style * stored_style = m_style_manager.get_stored_style();
|
||||||
|
const double *stored = ((stored_style!=nullptr)? &stored_style->projection.depth : nullptr);
|
||||||
|
const std::string revert_emboss_depth = _u8L("Revert embossed depth.");
|
||||||
|
const char *size_format = ((use_inch) ? "%.3f in" : "%.2f mm");
|
||||||
|
const std::string name = m_gui_cfg->translations.depth;
|
||||||
|
if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1, 1., size_format, use_inch, m_scale_depth)){
|
||||||
// size can't be zero or negative
|
// size can't be zero or negative
|
||||||
apply(value, limits.emboss);
|
apply(value, limits.emboss);
|
||||||
|
|
||||||
// only different value need process
|
// only different value need process
|
||||||
return !is_approx(value, m_volume->text_configuration->style.prop.emboss);
|
if(!is_approx(value, m_volume->emboss_shape->projection.depth))
|
||||||
}
|
|
||||||
|
|
||||||
void GLGizmoEmboss::draw_depth(bool use_inch)
|
|
||||||
{
|
|
||||||
float &value = m_style_manager.get_style().prop.emboss;
|
|
||||||
const EmbossStyle* stored_style = m_style_manager.get_stored_style();
|
|
||||||
const float *stored = ((stored_style)? &stored_style->prop.emboss : nullptr);
|
|
||||||
const std::string revert_emboss_depth = _u8L("Revert embossed depth.");
|
|
||||||
const char *size_format = ((use_inch) ? "%.3f in" : "%.2f mm");
|
|
||||||
const std::string name = m_gui_cfg->translations.depth;
|
|
||||||
if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1f, 1.f, size_format, use_inch, m_scale_depth))
|
|
||||||
if (set_depth())
|
|
||||||
process();
|
process();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool GLGizmoEmboss::rev_slider(const std::string &name,
|
bool GLGizmoEmboss::rev_slider(const std::string &name,
|
||||||
std::optional<int>& value,
|
std::optional<int>& value,
|
||||||
@ -2448,7 +2290,7 @@ bool GLGizmoEmboss::rev_slider(const std::string &name,
|
|||||||
int v_min,
|
int v_min,
|
||||||
int v_max,
|
int v_max,
|
||||||
const std::string& format,
|
const std::string& format,
|
||||||
const wxString &tooltip)
|
const wxString &tooltip) const
|
||||||
{
|
{
|
||||||
auto draw_slider_optional_int = [&]() -> bool {
|
auto draw_slider_optional_int = [&]() -> bool {
|
||||||
float slider_offset = m_gui_cfg->advanced_input_offset;
|
float slider_offset = m_gui_cfg->advanced_input_offset;
|
||||||
@ -2470,7 +2312,7 @@ bool GLGizmoEmboss::rev_slider(const std::string &name,
|
|||||||
float v_min,
|
float v_min,
|
||||||
float v_max,
|
float v_max,
|
||||||
const std::string& format,
|
const std::string& format,
|
||||||
const wxString &tooltip)
|
const wxString &tooltip) const
|
||||||
{
|
{
|
||||||
auto draw_slider_optional_float = [&]() -> bool {
|
auto draw_slider_optional_float = [&]() -> bool {
|
||||||
float slider_offset = m_gui_cfg->advanced_input_offset;
|
float slider_offset = m_gui_cfg->advanced_input_offset;
|
||||||
@ -2492,7 +2334,7 @@ bool GLGizmoEmboss::rev_slider(const std::string &name,
|
|||||||
float v_min,
|
float v_min,
|
||||||
float v_max,
|
float v_max,
|
||||||
const std::string &format,
|
const std::string &format,
|
||||||
const wxString &tooltip)
|
const wxString &tooltip) const
|
||||||
{
|
{
|
||||||
auto draw_slider_float = [&]() -> bool {
|
auto draw_slider_float = [&]() -> bool {
|
||||||
float slider_offset = m_gui_cfg->advanced_input_offset;
|
float slider_offset = m_gui_cfg->advanced_input_offset;
|
||||||
@ -2780,9 +2622,10 @@ ImVec2 GLGizmoEmboss::get_minimal_window_size() const
|
|||||||
else
|
else
|
||||||
res = m_gui_cfg->minimal_window_size_with_collections;
|
res = m_gui_cfg->minimal_window_size_with_collections;
|
||||||
|
|
||||||
bool is_object = m_volume->get_object()->volumes.size() == 1;
|
// Can change type of volume
|
||||||
if (!is_object)
|
if (!m_volume->is_the_only_one_part())
|
||||||
res.y += m_gui_cfg->height_of_volume_type_selector;
|
res.y += m_gui_cfg->height_of_volume_type_selector;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3033,18 +2876,17 @@ bool draw_button(const IconManager::VIcons &icons, IconType type, bool disable){
|
|||||||
get_icon(icons, type, IconState::disabled),
|
get_icon(icons, type, IconState::disabled),
|
||||||
disable);}
|
disable);}
|
||||||
|
|
||||||
TextDataBase::TextDataBase(DataBase &&parent, const FontFileWithCache &font_file, TextConfiguration &&text_configuration)
|
TextDataBase::TextDataBase(DataBase &&parent,
|
||||||
: DataBase(std::move(parent)), font_file(font_file) /* copy */, text_configuration(std::move(text_configuration))
|
const FontFileWithCache &font_file,
|
||||||
|
TextConfiguration &&text_configuration,
|
||||||
|
const EmbossProjection &projection)
|
||||||
|
: DataBase(std::move(parent)), m_font_file(font_file) /* copy */, m_text_configuration(std::move(text_configuration))
|
||||||
{
|
{
|
||||||
assert(this->font_file.has_value());
|
assert(m_font_file.has_value());
|
||||||
|
shape.projection = projection; // copy
|
||||||
|
|
||||||
// partialy fill shape from text configuration
|
const FontProp &fp = m_text_configuration.style.prop;
|
||||||
const FontProp &fp = this->text_configuration.style.prop;
|
const FontFile &ff = *m_font_file.font_file;
|
||||||
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);
|
shape.scale = get_text_shape_scale(fp, ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3054,36 +2896,37 @@ EmbossShape &TextDataBase::create_shape()
|
|||||||
return shape;
|
return shape;
|
||||||
|
|
||||||
// create shape by configuration
|
// create shape by configuration
|
||||||
const char *text = text_configuration.text.c_str();
|
const char *text = m_text_configuration.text.c_str();
|
||||||
const FontProp &fp = text_configuration.style.prop;
|
const FontProp &fp = m_text_configuration.style.prop;
|
||||||
auto was_canceled = [&c = cancel]() -> bool { return c->load(); };
|
auto was_canceled = [&c = cancel](){ return c->load(); };
|
||||||
shape.shapes = text2shapes(font_file, text, fp, was_canceled);
|
shape.shapes = text2shapes(m_font_file, text, fp, was_canceled);
|
||||||
|
|
||||||
// TEST
|
|
||||||
const FontProp &prop = text_configuration.style.prop;
|
|
||||||
const std::optional<unsigned int> &cn = prop.collection_number;
|
|
||||||
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
|
||||||
const FontFileWithCache &font = font_file;
|
|
||||||
assert(font_index < font.font_file->infos.size());
|
|
||||||
int unit_per_em = font.font_file->infos[font_index].unit_per_em;
|
|
||||||
float scale = prop.size_in_mm / unit_per_em;
|
|
||||||
float depth = prop.emboss / scale;
|
|
||||||
|
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextDataBase::write(ModelVolume &volume) const
|
void TextDataBase::write(ModelVolume &volume) const
|
||||||
{
|
{
|
||||||
volume.text_configuration = text_configuration; // copy
|
|
||||||
|
|
||||||
// discard information about rotation, should not be stored in volume
|
|
||||||
volume.text_configuration->style.prop.angle.reset();
|
|
||||||
|
|
||||||
// only temporary solution
|
|
||||||
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);
|
DataBase::write(volume);
|
||||||
|
volume.text_configuration = m_text_configuration; // copy
|
||||||
|
|
||||||
|
// TEMPORARY check until depricated variable will be deleted
|
||||||
|
FontProp &fp = volume.text_configuration->style.prop;
|
||||||
|
// Distance and angle are calculated on the fly it should not be stored in volume
|
||||||
|
assert(!fp.angle.has_value());
|
||||||
|
if (fp.angle.has_value())
|
||||||
|
fp.angle.reset();
|
||||||
|
assert(!fp.distance.has_value());
|
||||||
|
if (fp.distance.has_value())
|
||||||
|
fp.distance.reset();
|
||||||
|
|
||||||
|
// use_surface and emboss are stored in projection
|
||||||
|
assert(volume.emboss_shape.has_value());
|
||||||
|
if (!volume.emboss_shape.has_value())
|
||||||
|
return;
|
||||||
|
const EmbossProjection &ep = volume.emboss_shape->projection;
|
||||||
|
if (fp.use_surface != ep.use_surface)
|
||||||
|
fp.use_surface = ep.use_surface;
|
||||||
|
if (!is_approx(fp.emboss, static_cast<float>(ep.depth)))
|
||||||
|
fp.emboss = static_cast<float>(ep.depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DataBase> create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr<std::atomic<bool>>& cancel)
|
std::unique_ptr<DataBase> create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr<std::atomic<bool>>& cancel)
|
||||||
@ -3099,11 +2942,11 @@ std::unique_ptr<DataBase> create_emboss_data_base(const std::string &text, Style
|
|||||||
style_manager.load_valid_style();
|
style_manager.load_valid_style();
|
||||||
assert(style_manager.is_active_font());
|
assert(style_manager.is_active_font());
|
||||||
|
|
||||||
const EmbossStyle &es = style_manager.get_style();
|
const StyleManager::Style &style = style_manager.get_style();
|
||||||
// actualize font path - during changes in gui it could be corrupted
|
// actualize font path - during changes in gui it could be corrupted
|
||||||
// volume must store valid path
|
// volume must store valid path
|
||||||
assert(style_manager.get_wx_font().IsOk());
|
assert(style_manager.get_wx_font().IsOk());
|
||||||
assert(es.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0);
|
assert(style.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0);
|
||||||
|
|
||||||
// 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
|
||||||
@ -3114,8 +2957,8 @@ std::unique_ptr<DataBase> create_emboss_data_base(const std::string &text, Style
|
|||||||
cancel = std::make_shared<std::atomic<bool>>(false);
|
cancel = std::make_shared<std::atomic<bool>>(false);
|
||||||
DataBase base(volume_name, cancel);
|
DataBase base(volume_name, cancel);
|
||||||
FontFileWithCache &font = style_manager.get_font_file_with_cache();
|
FontFileWithCache &font = style_manager.get_font_file_with_cache();
|
||||||
TextConfiguration tc{es, text};
|
TextConfiguration tc{static_cast<EmbossStyle>(style), text};
|
||||||
return std::make_unique<TextDataBase>(std::move(base), font, std::move(tc));
|
return std::make_unique<TextDataBase>(std::move(base), font, std::move(tc), style.projection);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateVolumeParams create_input(GLCanvas3D &canvas, const StyleManager::Style &style, RaycastManager& raycaster, ModelVolumeType volume_type)
|
CreateVolumeParams create_input(GLCanvas3D &canvas, const StyleManager::Style &style, RaycastManager& raycaster, ModelVolumeType volume_type)
|
||||||
@ -3163,7 +3006,7 @@ bool store(const Facenames &facenames) {
|
|||||||
::cereal::BinaryOutputArchive archive(file);
|
::cereal::BinaryOutputArchive archive(file);
|
||||||
std::vector<wxString> good;
|
std::vector<wxString> good;
|
||||||
good.reserve(facenames.faces.size());
|
good.reserve(facenames.faces.size());
|
||||||
for (const ::FaceName &face : facenames.faces) good.push_back(face.wx_name);
|
for (const FaceName &face : facenames.faces) good.push_back(face.wx_name);
|
||||||
FacenamesSerializer data = {facenames.hash, good, facenames.bad};
|
FacenamesSerializer data = {facenames.hash, good, facenames.bad};
|
||||||
|
|
||||||
assert(std::is_sorted(data.bad.begin(), data.bad.end()));
|
assert(std::is_sorted(data.bad.begin(), data.bad.end()));
|
||||||
@ -3209,7 +3052,7 @@ bool load(Facenames &facenames) {
|
|||||||
|
|
||||||
void init_truncated_names(Facenames &face_names, float max_width)
|
void init_truncated_names(Facenames &face_names, float max_width)
|
||||||
{
|
{
|
||||||
for (::FaceName &face : face_names.faces) {
|
for (FaceName &face : face_names.faces) {
|
||||||
std::string name_str(face.wx_name.ToUTF8().data());
|
std::string name_str(face.wx_name.ToUTF8().data());
|
||||||
face.name_truncated = ImGuiWrapper::trunc(name_str, max_width);
|
face.name_truncated = ImGuiWrapper::trunc(name_str, max_width);
|
||||||
}
|
}
|
||||||
@ -3345,7 +3188,7 @@ void draw_font_preview(FaceName &face, const std::string& text, Facenames &faces
|
|||||||
size_t texture_index = (faces.texture_index + 1) % faces.count_cached_textures;
|
size_t texture_index = (faces.texture_index + 1) % faces.count_cached_textures;
|
||||||
|
|
||||||
// set previous cach as deleted
|
// set previous cach as deleted
|
||||||
for (::FaceName &f : faces.faces)
|
for (FaceName &f : faces.faces)
|
||||||
if (f.texture_index == texture_index) {
|
if (f.texture_index == texture_index) {
|
||||||
if (f.cancel != nullptr) f.cancel->store(true);
|
if (f.cancel != nullptr) f.cancel->store(true);
|
||||||
f.is_created = nullptr;
|
f.is_created = nullptr;
|
||||||
@ -3388,6 +3231,102 @@ void draw_font_preview(FaceName &face, const std::string& text, Facenames &faces
|
|||||||
ImGui::Image(tex_id, size, uv0, uv1);
|
ImGui::Image(tex_id, size, uv0, uv1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GuiCfg create_gui_configuration()
|
||||||
|
{
|
||||||
|
GuiCfg cfg; // initialize by default values;
|
||||||
|
|
||||||
|
float line_height = ImGui::GetTextLineHeight();
|
||||||
|
float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing();
|
||||||
|
float space = line_height_with_spacing - line_height;
|
||||||
|
const ImGuiStyle &style = ImGui::GetStyle();
|
||||||
|
|
||||||
|
cfg.max_style_name_width = ImGui::CalcTextSize("Maximal font name, extended").x;
|
||||||
|
|
||||||
|
cfg.icon_width = static_cast<unsigned int>(std::ceil(line_height));
|
||||||
|
// make size pair number
|
||||||
|
if (cfg.icon_width % 2 != 0) ++cfg.icon_width;
|
||||||
|
|
||||||
|
cfg.delete_pos_x = cfg.max_style_name_width + space;
|
||||||
|
const float count_line_of_text = 3.f;
|
||||||
|
cfg.text_size = ImVec2(-FLT_MIN, line_height_with_spacing * count_line_of_text);
|
||||||
|
ImVec2 letter_m_size = ImGui::CalcTextSize("M");
|
||||||
|
const float count_letter_M_in_input = 12.f;
|
||||||
|
cfg.input_width = letter_m_size.x * count_letter_M_in_input;
|
||||||
|
GuiCfg::Translations &tr = cfg.translations;
|
||||||
|
|
||||||
|
tr.font = _u8L("Font");
|
||||||
|
tr.height = _u8L("Height");
|
||||||
|
tr.depth = _u8L("Depth");
|
||||||
|
|
||||||
|
float max_text_width = std::max({
|
||||||
|
ImGui::CalcTextSize(tr.font.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.height.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.depth.c_str()).x});
|
||||||
|
cfg.indent = static_cast<float>(cfg.icon_width);
|
||||||
|
cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space;
|
||||||
|
|
||||||
|
tr.use_surface = _u8L("Use surface");
|
||||||
|
tr.char_gap = _u8L("Char gap");
|
||||||
|
tr.line_gap = _u8L("Line gap");
|
||||||
|
tr.boldness = _u8L("Boldness");
|
||||||
|
tr.skew_ration = _u8L("Skew ratio");
|
||||||
|
tr.from_surface = _u8L("From surface");
|
||||||
|
tr.rotation = _u8L("Rotation");
|
||||||
|
tr.keep_up = _u8L("Keep Up");
|
||||||
|
tr.collection = _u8L("Collection");
|
||||||
|
|
||||||
|
float max_advanced_text_width = std::max({
|
||||||
|
ImGui::CalcTextSize(tr.use_surface.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.char_gap.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.line_gap.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.boldness.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.skew_ration.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.from_surface.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.rotation.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.keep_up.c_str()).x,
|
||||||
|
ImGui::CalcTextSize(tr.collection.c_str()).x });
|
||||||
|
cfg.advanced_input_offset = max_advanced_text_width
|
||||||
|
+ 3 * space + cfg.indent;
|
||||||
|
|
||||||
|
// calculate window size
|
||||||
|
float window_title = line_height + 2*style.FramePadding.y + 2 * style.WindowTitleAlign.y;
|
||||||
|
float input_height = line_height_with_spacing + 2*style.FramePadding.y;
|
||||||
|
float tree_header = line_height_with_spacing;
|
||||||
|
float separator_height = 1 + style.FramePadding.y;
|
||||||
|
|
||||||
|
// "Text is to object" + radio buttons
|
||||||
|
cfg.height_of_volume_type_selector = separator_height + line_height_with_spacing + input_height;
|
||||||
|
|
||||||
|
float window_height =
|
||||||
|
window_title + // window title
|
||||||
|
cfg.text_size.y + // text field
|
||||||
|
input_height * 4 + // font name + height + depth + style selector
|
||||||
|
tree_header + // advance tree
|
||||||
|
separator_height + // presets separator line
|
||||||
|
line_height_with_spacing + // "Presets"
|
||||||
|
2 * style.WindowPadding.y;
|
||||||
|
float window_width = cfg.input_offset + cfg.input_width + 2*style.WindowPadding.x
|
||||||
|
+ 2 * (cfg.icon_width + space);
|
||||||
|
cfg.minimal_window_size = ImVec2(window_width, window_height);
|
||||||
|
|
||||||
|
// 9 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, keepUp, textFaceToCamera
|
||||||
|
// 4 = 1px for fix each edit image of drag float
|
||||||
|
float advance_height = input_height * 9 + 8;
|
||||||
|
cfg.minimal_window_size_with_advance =
|
||||||
|
ImVec2(cfg.minimal_window_size.x,
|
||||||
|
cfg.minimal_window_size.y + advance_height);
|
||||||
|
|
||||||
|
cfg.minimal_window_size_with_collections =
|
||||||
|
ImVec2(cfg.minimal_window_size_with_advance.x,
|
||||||
|
cfg.minimal_window_size_with_advance.y + input_height);
|
||||||
|
|
||||||
|
int max_style_image_width = static_cast<int>(std::round(cfg.max_style_name_width/2 - 2 * style.FramePadding.x));
|
||||||
|
int max_style_image_height = static_cast<int>(std::round(1.5 * input_height));
|
||||||
|
cfg.max_style_image_size = Vec2i(max_style_image_width, max_style_image_height);
|
||||||
|
cfg.face_name_size.y() = line_height_with_spacing;
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// any existing icon filename to not influence GUI
|
// any existing icon filename to not influence GUI
|
||||||
|
@ -105,35 +105,32 @@ private:
|
|||||||
|
|
||||||
// call after set m_style_manager.get_style().prop.size_in_mm
|
// call after set m_style_manager.get_style().prop.size_in_mm
|
||||||
bool set_height();
|
bool set_height();
|
||||||
// call after set m_style_manager.get_style().prop.emboss
|
|
||||||
bool set_depth();
|
|
||||||
|
|
||||||
bool draw_italic_button();
|
bool draw_italic_button();
|
||||||
bool draw_bold_button();
|
bool draw_bold_button();
|
||||||
void draw_advanced();
|
void draw_advanced();
|
||||||
|
|
||||||
bool select_facename(const wxString& facename);
|
bool select_facename(const wxString& facename);
|
||||||
bool rev_input_mm(const std::string &name, float &value, const float *default_value,
|
|
||||||
const std::string &undo_tooltip, float step, float step_fast, const char *format,
|
template<typename T> bool rev_input_mm(const std::string &name, T &value, const T *default_value,
|
||||||
bool use_inch, const std::optional<float>& scale);
|
const std::string &undo_tooltip, T step, T step_fast, const char *format, bool use_inch, const std::optional<float>& scale) const;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reversible input float with option to restor default value
|
/// Reversible input float with option to restor default value
|
||||||
/// TODO: make more general, static and move to ImGuiWrapper
|
/// TODO: make more general, static and move to ImGuiWrapper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True when value changed otherwise FALSE.</returns>
|
/// <returns>True when value changed otherwise FALSE.</returns>
|
||||||
bool rev_input(const std::string &name, float &value, const float *default_value,
|
template<typename T> bool rev_input(const std::string &name, T &value, const T *default_value,
|
||||||
const std::string &undo_tooltip, float step, float step_fast, const char *format,
|
const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags = 0) const;
|
||||||
ImGuiInputTextFlags flags = 0);
|
bool rev_checkbox(const std::string &name, bool &value, const bool* default_value, const std::string &undo_tooltip) const;
|
||||||
bool rev_checkbox(const std::string &name, bool &value, const bool* default_value, const std::string &undo_tooltip);
|
|
||||||
bool rev_slider(const std::string &name, std::optional<int>& value, const std::optional<int> *default_value,
|
bool rev_slider(const std::string &name, std::optional<int>& value, const std::optional<int> *default_value,
|
||||||
const std::string &undo_tooltip, int v_min, int v_max, const std::string &format, const wxString &tooltip);
|
const std::string &undo_tooltip, int v_min, int v_max, const std::string &format, const wxString &tooltip) const;
|
||||||
bool rev_slider(const std::string &name, std::optional<float>& value, const std::optional<float> *default_value,
|
bool rev_slider(const std::string &name, std::optional<float>& value, const std::optional<float> *default_value,
|
||||||
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip);
|
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip) const;
|
||||||
bool rev_slider(const std::string &name, float &value, const float *default_value,
|
bool rev_slider(const std::string &name, float &value, const float *default_value,
|
||||||
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip);
|
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip) const;
|
||||||
template<typename T, typename Draw>
|
template<typename T, typename Draw> bool revertible(const std::string &name, T &value, const T *default_value,
|
||||||
bool revertible(const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, float undo_offset, Draw draw);
|
const std::string &undo_tooltip, float undo_offset, Draw draw) const;
|
||||||
|
|
||||||
bool m_should_set_minimal_windows_size = false;
|
bool m_should_set_minimal_windows_size = false;
|
||||||
void set_minimal_window_size(bool is_advance_edit_style);
|
void set_minimal_window_size(bool is_advance_edit_style);
|
||||||
@ -151,7 +148,6 @@ private:
|
|||||||
|
|
||||||
struct GuiCfg;
|
struct GuiCfg;
|
||||||
std::unique_ptr<const GuiCfg> m_gui_cfg = nullptr;
|
std::unique_ptr<const GuiCfg> m_gui_cfg = nullptr;
|
||||||
static GuiCfg create_gui_configuration();
|
|
||||||
|
|
||||||
// Is open tree with advanced options
|
// Is open tree with advanced options
|
||||||
bool m_is_advanced_edit_style = false;
|
bool m_is_advanced_edit_style = false;
|
||||||
|
@ -315,9 +315,7 @@ void CreateObjectJob::process(Ctl &ctl)
|
|||||||
// mouse pose is out of build plate so create object in center of plate
|
// mouse pose is out of build plate so create object in center of plate
|
||||||
bed_coor = bed.centroid().cast<double>();
|
bed_coor = bed.centroid().cast<double>();
|
||||||
|
|
||||||
// TODO: need TextConfiguration refactor to work !!!
|
|
||||||
double z = m_input.base->shape.projection.depth / 2;
|
double z = m_input.base->shape.projection.depth / 2;
|
||||||
|
|
||||||
Vec3d offset(bed_coor.x(), bed_coor.y(), z);
|
Vec3d offset(bed_coor.x(), bed_coor.y(), z);
|
||||||
offset -= m_result.center();
|
offset -= m_result.center();
|
||||||
Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z());
|
Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z());
|
||||||
|
@ -7,6 +7,12 @@
|
|||||||
#include "slic3r/GUI/CameraUtils.hpp"
|
#include "slic3r/GUI/CameraUtils.hpp"
|
||||||
#include "libslic3r/Emboss.hpp"
|
#include "libslic3r/Emboss.hpp"
|
||||||
|
|
||||||
|
namespace{
|
||||||
|
// Distance of embossed volume from surface to be represented as distance surface
|
||||||
|
// Maximal distance is also enlarge by size of emboss depth
|
||||||
|
constexpr Slic3r::MinMax<double> surface_distance_sq{1e-4, 10.}; // [in mm]
|
||||||
|
}
|
||||||
|
|
||||||
namespace Slic3r::GUI {
|
namespace Slic3r::GUI {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -19,14 +25,15 @@ namespace Slic3r::GUI {
|
|||||||
static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera)
|
static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera)
|
||||||
{
|
{
|
||||||
const Transform3d &volume_tr = volume.get_matrix();
|
const Transform3d &volume_tr = volume.get_matrix();
|
||||||
assert(volume.text_configuration.has_value());
|
assert(volume.emboss_shape.has_value());
|
||||||
|
|
||||||
auto calc_offset = [&screen_coor, &volume_tr, &camera, &volume](const Transform3d &instrance_tr) -> Vec2d {
|
auto calc_offset = [&screen_coor, &volume_tr, &camera, &volume](const Transform3d &instrance_tr) -> Vec2d {
|
||||||
Transform3d to_world = instrance_tr * volume_tr;
|
Transform3d to_world = instrance_tr * volume_tr;
|
||||||
|
|
||||||
// Use fix of .3mf loaded tranformation when exist
|
// Use fix of .3mf loaded tranformation when exist
|
||||||
if (volume.text_configuration->fix_3mf_tr.has_value())
|
if (std::optional<Transform3d> fix = volume.emboss_shape->fix_3mf_tr;
|
||||||
to_world = to_world * (*volume.text_configuration->fix_3mf_tr);
|
fix.has_value())
|
||||||
|
to_world = to_world * (*fix);
|
||||||
// zero point of volume in world coordinate system
|
// zero point of volume in world coordinate system
|
||||||
Vec3d volume_center = to_world.translation();
|
Vec3d volume_center = to_world.translation();
|
||||||
// screen coordinate of volume center
|
// screen coordinate of volume center
|
||||||
@ -152,11 +159,12 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
|
|||||||
|
|
||||||
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;
|
|
||||||
// fix baked transformation from .3mf store process
|
// fix baked transformation from .3mf store process
|
||||||
if (tc.fix_3mf_tr.has_value())
|
if (const std::optional<EmbossShape> &es_opt = volume->emboss_shape;
|
||||||
volume_tr = volume_tr * tc.fix_3mf_tr->inverse();
|
es_opt.has_value()) {
|
||||||
|
const std::optional<Slic3r::Transform3d> &fix = es_opt->fix_3mf_tr;
|
||||||
|
if (fix.has_value())
|
||||||
|
volume_tr = volume_tr * fix->inverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform3d instance_tr = instance->get_matrix();
|
Transform3d instance_tr = instance->get_matrix();
|
||||||
@ -165,7 +173,11 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
|
|||||||
std::optional<float> start_angle;
|
std::optional<float> start_angle;
|
||||||
if (up_limit.has_value())
|
if (up_limit.has_value())
|
||||||
start_angle = Emboss::calc_up(world_tr, *up_limit);
|
start_angle = Emboss::calc_up(world_tr, *up_limit);
|
||||||
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume_ptr, condition, start_angle};
|
|
||||||
|
std::optional<float> start_distance;
|
||||||
|
if (!volume->emboss_shape->projection.use_surface)
|
||||||
|
start_distance = calc_distance(gl_volume, raycast_manager, &condition);
|
||||||
|
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume_ptr, condition, start_angle, start_distance};
|
||||||
|
|
||||||
// disable moving with object by mouse
|
// disable moving with object by mouse
|
||||||
canvas.enable_moving(false);
|
canvas.enable_moving(false);
|
||||||
@ -235,14 +247,14 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
|
|||||||
assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value());
|
assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value());
|
||||||
|
|
||||||
const ModelVolume *volume = get_model_volume(*surface_drag->gl_volume, canvas.get_model()->objects);
|
const ModelVolume *volume = get_model_volume(*surface_drag->gl_volume, canvas.get_model()->objects);
|
||||||
if (volume != nullptr && volume->text_configuration.has_value()) {
|
|
||||||
const TextConfiguration &tc = *volume->text_configuration;
|
|
||||||
// fix baked transformation from .3mf store process
|
// fix baked transformation from .3mf store process
|
||||||
if (tc.fix_3mf_tr.has_value())
|
if (volume != nullptr && volume->emboss_shape.has_value()) {
|
||||||
volume_new = volume_new * (*tc.fix_3mf_tr);
|
const std::optional<Slic3r::Transform3d> &fix = volume->emboss_shape->fix_3mf_tr;
|
||||||
|
if (fix.has_value())
|
||||||
|
volume_new = volume_new * (*fix);
|
||||||
|
|
||||||
// apply move in Z direction and rotation by up vector
|
// apply move in Z direction and rotation by up vector
|
||||||
Emboss::apply_transformation(surface_drag->start_angle, tc.style.prop.distance, volume_new);
|
Emboss::apply_transformation(surface_drag->start_angle, surface_drag->start_distance, volume_new);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update transformation for all instances
|
// Update transformation for all instances
|
||||||
@ -317,6 +329,67 @@ std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastMana
|
|||||||
return offset_volume;
|
return offset_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return calc_distance(gl_volume, raycaster, &condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<float> calc_distance(const GLVolume &gl_volume, const RaycastManager &raycaster, const RaycastManager::ISkip *condition)
|
||||||
|
{
|
||||||
|
Transform3d w = gl_volume.world_matrix();
|
||||||
|
Vec3d p = w.translation();
|
||||||
|
const Vec3d& dir = get_z_base(w);
|
||||||
|
auto hit_opt = raycaster.closest_hit(p, dir, condition);
|
||||||
|
if (!hit_opt.has_value())
|
||||||
|
return {};
|
||||||
|
const RaycastManager::Hit &hit = *hit_opt;
|
||||||
|
|
||||||
|
// too small distance is calculated as zero distance
|
||||||
|
if (hit.squared_distance < ::surface_distance_sq.min)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// check maximal distance
|
||||||
|
const BoundingBoxf3& bb = gl_volume.bounding_box();
|
||||||
|
double max_squared_distance = std::max(std::pow(2 * bb.size().z(), 2), ::surface_distance_sq.max);
|
||||||
|
if (hit.squared_distance > max_squared_distance)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// calculate sign
|
||||||
|
float sign = ((hit.position - p).dot(dir) > 0)? 1.f : -1.f;
|
||||||
|
|
||||||
|
// distiguish sign
|
||||||
|
return sign * static_cast<float>(sqrt(hit.squared_distance));
|
||||||
|
}
|
||||||
|
|
||||||
Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects)
|
Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects)
|
||||||
{
|
{
|
||||||
Transform3d res = gl_volume.world_matrix();
|
Transform3d res = gl_volume.world_matrix();
|
||||||
@ -325,11 +398,11 @@ Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs
|
|||||||
if (!mv)
|
if (!mv)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
const std::optional<TextConfiguration> &tc = mv->text_configuration;
|
const std::optional<EmbossShape> &es = mv->emboss_shape;
|
||||||
if (!tc.has_value())
|
if (!es.has_value())
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
const std::optional<Transform3d> &fix = tc->fix_3mf_tr;
|
const std::optional<Transform3d> &fix = es->fix_3mf_tr;
|
||||||
if (!fix.has_value())
|
if (!fix.has_value())
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
@ -37,6 +37,9 @@ struct SurfaceDrag
|
|||||||
// initial rotation in Z axis of volume
|
// initial rotation in Z axis of volume
|
||||||
std::optional<float> start_angle;
|
std::optional<float> start_angle;
|
||||||
|
|
||||||
|
// initial Z distance from surface
|
||||||
|
std::optional<float> start_distance;
|
||||||
|
|
||||||
// Flag whether coordinate hit some volume
|
// Flag whether coordinate hit some volume
|
||||||
bool exist_hit = true;
|
bool exist_hit = true;
|
||||||
};
|
};
|
||||||
@ -72,6 +75,16 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
|
|||||||
/// <returns>Offset of volume in volume coordinate</returns>
|
/// <returns>Offset of volume in volume coordinate</returns>
|
||||||
std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager);
|
std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate distance by ray to surface of object in emboss direction
|
||||||
|
/// </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);
|
||||||
|
std::optional<float> calc_distance(const GLVolume &gl_volume, const RaycastManager &raycaster, const RaycastManager::ISkip *condition);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get transformation to world
|
/// Get transformation to world
|
||||||
/// - use fix after store to 3mf when exists
|
/// - use fix after store to 3mf when exists
|
||||||
|
Loading…
x
Reference in New Issue
Block a user