mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 12:35:55 +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,
|
||||
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
|
||||
Point to_point(const stbtt__point &point);
|
||||
@ -797,8 +797,7 @@ const Glyph* priv::get_glyph(
|
||||
auto glyph_item = cache.find(unicode);
|
||||
if (glyph_item != cache.end()) return &glyph_item->second;
|
||||
|
||||
unsigned int font_index = font_prop.collection_number.has_value()?
|
||||
*font_prop.collection_number : 0;
|
||||
unsigned int font_index = font_prop.collection_number.value_or(0);
|
||||
if (!is_valid(font, font_index)) return nullptr;
|
||||
|
||||
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
|
||||
if (flatness < RESOLUTION) flatness = RESOLUTION;
|
||||
|
||||
std::optional<Glyph> glyph_opt =
|
||||
priv::get_glyph(*font_info_opt, unicode, flatness);
|
||||
std::optional<Glyph> glyph_opt = priv::get_glyph(*font_info_opt, unicode, flatness);
|
||||
|
||||
// IMPROVE: multiple loadig glyph without data
|
||||
// has definition inside of font?
|
||||
if (!glyph_opt.has_value()) return nullptr;
|
||||
|
||||
Glyph &glyph = *glyph_opt;
|
||||
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
|
||||
glyph_opt->advance_width =
|
||||
static_cast<int>(glyph_opt->advance_width / SHAPE_SCALE);
|
||||
glyph_opt->left_side_bearing =
|
||||
static_cast<int>(glyph_opt->left_side_bearing / SHAPE_SCALE);
|
||||
glyph.advance_width = static_cast<int>(glyph.advance_width / SHAPE_SCALE);
|
||||
glyph.left_side_bearing = static_cast<int>(glyph.left_side_bearing / SHAPE_SCALE);
|
||||
|
||||
if (!glyph_opt->shape.empty()) {
|
||||
if (!glyph.shape.empty()) {
|
||||
if (font_prop.boldness.has_value()) {
|
||||
float delta = *font_prop.boldness / SHAPE_SCALE /
|
||||
font_prop.size_in_mm;
|
||||
glyph_opt->shape = Slic3r::union_ex(offset_ex(glyph_opt->shape, delta));
|
||||
float delta = static_cast<float>(*font_prop.boldness / SHAPE_SCALE / font_prop.size_in_mm);
|
||||
glyph.shape = Slic3r::union_ex(offset_ex(glyph.shape, delta));
|
||||
}
|
||||
if (font_prop.skew.has_value()) {
|
||||
const float &ratio = *font_prop.skew;
|
||||
auto skew = [&ratio](Polygon &polygon) {
|
||||
for (Slic3r::Point &p : polygon.points) {
|
||||
p.x() += p.y() * ratio;
|
||||
}
|
||||
for (Slic3r::Point &p : polygon.points)
|
||||
p.x() += static_cast<int>(std::round(p.y() * ratio));
|
||||
};
|
||||
for (ExPolygon &expolygon : glyph_opt->shape) {
|
||||
for (ExPolygon &expolygon : glyph.shape) {
|
||||
skew(expolygon.contour);
|
||||
for (Polygon &hole : expolygon.holes) skew(hole);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto it = cache.insert({unicode, std::move(*glyph_opt)});
|
||||
assert(it.second);
|
||||
return &it.first->second;
|
||||
auto [it, success] = cache.try_emplace(unicode, std::move(glyph));
|
||||
assert(success);
|
||||
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()),
|
||||
boost::nowide::narrow(path.c_str()),
|
||||
EmbossStyle::Type::file_path, FontProp() };
|
||||
@ -1220,8 +1215,7 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache,
|
||||
Point cursor(0, 0);
|
||||
ExPolygons result;
|
||||
const FontFile& font = *font_with_cache.font_file;
|
||||
unsigned int font_index = font_prop.collection_number.has_value()?
|
||||
*font_prop.collection_number : 0;
|
||||
unsigned int font_index = font_prop.collection_number.value_or(0);
|
||||
if (!priv::is_valid(font, font_index)) return {};
|
||||
const FontFile::Info& info = font.infos[font_index];
|
||||
Glyphs& cache = *font_with_cache.cache;
|
||||
|
@ -36,6 +36,7 @@ namespace pt = boost::property_tree;
|
||||
#include "miniz_extension.hpp"
|
||||
|
||||
#include "TextConfiguration.hpp"
|
||||
#include "EmbossShape.hpp"
|
||||
|
||||
#include <fast_float/fast_float.h>
|
||||
|
||||
|
@ -83,7 +83,7 @@ template<typename T> struct Limit {
|
||||
// Variable keep limits for variables
|
||||
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
|
||||
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
|
||||
@ -91,9 +91,6 @@ static const struct Limits
|
||||
MinMax<int> line_gap{-20000, 20000}; // in font points
|
||||
// distance text object from surface
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
@ -117,16 +114,17 @@ ImVec2 calc_fine_position(const Selection &selection, const ImVec2 &windows_size
|
||||
/// </summary>
|
||||
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
|
||||
EmbossShape &create_shape() override;
|
||||
void write(ModelVolume &volume) const override;
|
||||
|
||||
// private:
|
||||
private:
|
||||
// Keep pointer on Data of font (glyph shapes)
|
||||
FontFileWithCache font_file;
|
||||
FontFileWithCache m_font_file;
|
||||
// font item is not used for create object
|
||||
TextConfiguration text_configuration;
|
||||
TextConfiguration m_text_configuration;
|
||||
};
|
||||
|
||||
// Loaded icons enum
|
||||
@ -270,6 +268,7 @@ struct GuiCfg
|
||||
};
|
||||
Translations translations;
|
||||
};
|
||||
GuiCfg create_gui_configuration();
|
||||
|
||||
void draw_font_preview(FaceName &face, const std::string &text, Facenames &faces, const GuiCfg &cfg, bool is_visible);
|
||||
} // 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
|
||||
) {
|
||||
// Create cache for gui offsets
|
||||
GuiCfg cfg = create_gui_configuration();
|
||||
::GuiCfg cfg = create_gui_configuration();
|
||||
cfg.screen_scale = screen_scale;
|
||||
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
|
||||
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); }
|
||||
|
||||
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()
|
||||
{
|
||||
wxFontEnumerator::InvalidateCache();
|
||||
@ -881,66 +785,48 @@ void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); }
|
||||
namespace {
|
||||
|
||||
/// <summary>
|
||||
/// Throow ray by embossing params to object and find surface point
|
||||
/// Check installed fonts whether optional face name exist in installed fonts
|
||||
/// </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)
|
||||
/// <param name="face_name_opt">Name from style - style.prop.face_name</param>
|
||||
/// <param name="face_names">All installed good and bad fonts - not const must be possible to initialize it</param>
|
||||
/// <returns>When it could be installed it contain value(potentionaly empty string)</returns>
|
||||
std::optional<wxString> get_installed_face_name(const std::optional<std::string> &face_name_opt, ::Facenames& face_names)
|
||||
{
|
||||
const ModelObject *object = get_model_object(gl_volume, canvas.get_model()->objects);
|
||||
assert(object != nullptr);
|
||||
if (object == nullptr)
|
||||
return {};
|
||||
// Could exist OS without getter on face_name,
|
||||
// but it is able to restore font from descriptor
|
||||
// Soo default value must be TRUE
|
||||
if (face_name_opt.has_value())
|
||||
return wxString();
|
||||
|
||||
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 {};
|
||||
wxString face_name(face_name_opt->c_str());
|
||||
|
||||
if (volume->is_the_only_one_part())
|
||||
return {};
|
||||
// search in enumerated fonts
|
||||
// refresh list of installed font in the OS.
|
||||
init_face_names(face_names);
|
||||
face_names.is_init = false;
|
||||
|
||||
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);
|
||||
auto cmp = [](const FaceName &fn, const wxString &wx_name) { return fn.wx_name < wx_name; };
|
||||
const std::vector<FaceName> &faces = face_names.faces;
|
||||
// is font installed?
|
||||
if (auto it = std::lower_bound(faces.begin(), faces.end(), face_name, cmp);
|
||||
it != faces.end() && it->wx_name == face_name)
|
||||
return face_name;
|
||||
|
||||
const std::vector<wxString> &bad = 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?
|
||||
) {
|
||||
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);
|
||||
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));
|
||||
}
|
||||
return {}; // not installed
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -981,46 +867,11 @@ void GLGizmoEmboss::set_volume_by_selection()
|
||||
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;
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::optional<wxString> installed_name = get_installed_face_name(style.prop.face_name, *m_face_names);
|
||||
|
||||
wxFont wx_font;
|
||||
// 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);
|
||||
|
||||
// Flag that is selected same font
|
||||
@ -1030,8 +881,8 @@ void GLGizmoEmboss::set_volume_by_selection()
|
||||
is_exact_font = false;
|
||||
// Try create similar wx font by FontFamily
|
||||
wx_font = WxFontUtils::create_wxFont(style);
|
||||
if (is_font_installed && !face_name.empty())
|
||||
is_exact_font = wx_font.SetFaceName(face_name);
|
||||
if (installed_name.has_value() && !installed_name->empty())
|
||||
is_exact_font = wx_font.SetFaceName(*installed_name);
|
||||
|
||||
// Have to use some wxFont
|
||||
if (!wx_font.IsOk())
|
||||
@ -1163,16 +1014,17 @@ bool GLGizmoEmboss::process()
|
||||
// exist loaded font file?
|
||||
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;
|
||||
|
||||
// check cutting from source mesh
|
||||
// TODO: do it better way
|
||||
FontProp &fp = static_cast<TextDataBase *>(data.base.get())->text_configuration.style.prop;
|
||||
bool &use_surface = fp.use_surface;
|
||||
bool is_object = m_volume->get_object()->volumes.size() == 1;
|
||||
if (use_surface && is_object)
|
||||
bool &use_surface = data.base->shape.projection.use_surface;
|
||||
if (use_surface && m_volume->is_the_only_one_part())
|
||||
use_surface = false;
|
||||
|
||||
if (use_surface) {
|
||||
@ -1181,12 +1033,12 @@ bool GLGizmoEmboss::process()
|
||||
if (sources.empty()) return false;
|
||||
|
||||
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())
|
||||
text_tr = text_tr * fix_3mf->inverse();
|
||||
|
||||
// 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);
|
||||
if (offset.has_value())
|
||||
text_tr *= Eigen::Translation<double, 3>(*offset);
|
||||
@ -1194,8 +1046,7 @@ bool GLGizmoEmboss::process()
|
||||
|
||||
bool is_outside = m_volume->is_model_part();
|
||||
// check that there is not unexpected volume type
|
||||
assert(is_outside || m_volume->is_negative_volume() ||
|
||||
m_volume->is_modifier());
|
||||
assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier());
|
||||
UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, is_outside, std::move(sources)}};
|
||||
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
|
||||
} else {
|
||||
@ -1215,8 +1066,8 @@ bool GLGizmoEmboss::process()
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace priv {
|
||||
static bool is_text_empty(const std::string &text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; }
|
||||
namespace {
|
||||
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()
|
||||
@ -1224,7 +1075,7 @@ void GLGizmoEmboss::close()
|
||||
// remove volume when text is empty
|
||||
if (m_volume != nullptr &&
|
||||
m_volume->text_configuration.has_value() &&
|
||||
priv::is_text_empty(m_text)) {
|
||||
is_text_empty(m_text)) {
|
||||
Plater &p = *wxGetApp().plater();
|
||||
// is the text object?
|
||||
if (m_volume->is_the_only_one_part()) {
|
||||
@ -1381,7 +1232,7 @@ void GLGizmoEmboss::draw_text_input()
|
||||
warning_tool_tip += "\n";
|
||||
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."));
|
||||
if (m_text_contain_unknown_glyph)
|
||||
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
|
||||
if (!warning_tool_tip.empty()) {
|
||||
// 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();
|
||||
float scrollbar_width = (input->ScrollbarY) ? style.ScrollbarSize : 0.f;
|
||||
float scrollbar_height = (input->ScrollbarX) ? style.ScrollbarSize : 0.f;
|
||||
|
||||
bool hovered = ImGui::IsItemHovered();
|
||||
if (hovered)
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", warning_tool_tip.c_str());
|
||||
|
||||
ImVec2 cursor = ImGui::GetCursorPos();
|
||||
@ -1443,7 +1292,7 @@ void GLGizmoEmboss::draw_text_input()
|
||||
// IMPROVE: only extend not clear
|
||||
// Extend font ranges
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1471,7 +1320,7 @@ void GLGizmoEmboss::init_font_name_texture() {
|
||||
glsafe(::glBindTexture(target, no_texture_id));
|
||||
|
||||
// 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.is_created = nullptr;
|
||||
}
|
||||
@ -1484,8 +1333,7 @@ bool GLGizmoEmboss::select_facename(const wxString &facename)
|
||||
{
|
||||
if (!wxFontEnumerator::IsValidFacename(facename)) return false;
|
||||
// Select font
|
||||
const wxFontEncoding &encoding = m_face_names->encoding;
|
||||
wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(encoding));
|
||||
wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(Facenames::encoding));
|
||||
if (!wx_font.IsOk()) return false;
|
||||
#ifdef USE_PIXEL_SIZE_IN_WX_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::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();
|
||||
|
||||
// font name to uppercase
|
||||
@ -1629,7 +1477,7 @@ void GLGizmoEmboss::draw_font_list()
|
||||
if (m_face_names->texture_id == 0)
|
||||
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;
|
||||
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->hide.clear();
|
||||
// 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)
|
||||
face.cancel->store(true);
|
||||
glsafe(::glDeleteTextures(1, &m_face_names->texture_id));
|
||||
@ -1769,7 +1617,7 @@ void GLGizmoEmboss::draw_model_type()
|
||||
m_volume->set_type(*new_type);
|
||||
|
||||
// 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
|
||||
bool is_volume_move_inside = (type == part);
|
||||
bool is_volume_move_outside = (*new_type == part);
|
||||
@ -1804,9 +1652,9 @@ void GLGizmoEmboss::draw_style_rename_popup() {
|
||||
|
||||
bool allow_change = false;
|
||||
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) {
|
||||
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 {
|
||||
ImGui::NewLine();
|
||||
allow_change = true;
|
||||
@ -1824,7 +1672,7 @@ void GLGizmoEmboss::draw_style_rename_popup() {
|
||||
|
||||
if (store) {
|
||||
// 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) {
|
||||
if (!mv->text_configuration.has_value()) continue;
|
||||
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 allow_change = false;
|
||||
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) {
|
||||
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 {
|
||||
ImGui::NewLine();
|
||||
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();
|
||||
draw_style_save_as_popup();
|
||||
ImGui::EndPopup();
|
||||
@ -2056,11 +1904,11 @@ void GLGizmoEmboss::draw_style_list() {
|
||||
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())
|
||||
ImGui::Text("%s", title.c_str());
|
||||
ImGui::Text("%s", presets.c_str());
|
||||
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);
|
||||
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 std::string &undo_tooltip,
|
||||
float undo_offset,
|
||||
Draw draw)
|
||||
Draw draw) const
|
||||
{
|
||||
bool changed = exist_change(value, default_value);
|
||||
if (changed || default_value == nullptr)
|
||||
@ -2301,43 +2149,43 @@ bool GLGizmoEmboss::revertible(const std::string &name,
|
||||
}
|
||||
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); }
|
||||
|
||||
|
||||
bool GLGizmoEmboss::rev_input(const std::string &name,
|
||||
float &value,
|
||||
const float *default_value,
|
||||
const std::string &undo_tooltip,
|
||||
float step,
|
||||
float step_fast,
|
||||
const char *format,
|
||||
ImGuiInputTextFlags flags)
|
||||
template<typename T>
|
||||
bool GLGizmoEmboss::rev_input(const std::string &name, T &value, const T *default_value,
|
||||
const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags) const
|
||||
{
|
||||
// draw offseted input
|
||||
auto draw_offseted_input = [&]()->bool{
|
||||
float input_offset = m_gui_cfg->input_offset;
|
||||
float input_width = m_gui_cfg->input_width;
|
||||
ImGui::SameLine(input_offset);
|
||||
ImGui::SetNextItemWidth(input_width);
|
||||
return ImGui::InputFloat(("##" + name).c_str(),
|
||||
auto draw_offseted_input = [&offset = m_gui_cfg->input_offset, &width = m_gui_cfg->input_width,
|
||||
&name, &value, &step, &step_fast, format, flags](){
|
||||
ImGui::SameLine(offset);
|
||||
ImGui::SetNextItemWidth(width);
|
||||
return imgui_input(("##" + name).c_str(),
|
||||
&value, step, step_fast, format, flags);
|
||||
};
|
||||
float undo_offset = ImGui::GetStyle().FramePadding.x;
|
||||
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,
|
||||
float &value,
|
||||
const float *default_value_ptr,
|
||||
T &value,
|
||||
const T *default_value_ptr,
|
||||
const std::string &undo_tooltip,
|
||||
float step,
|
||||
float step_fast,
|
||||
T step,
|
||||
T step_fast,
|
||||
const char *format,
|
||||
bool use_inch,
|
||||
const std::optional<float>& scale)
|
||||
const std::optional<float> &scale) const
|
||||
{
|
||||
// _variable which temporary keep value
|
||||
float value_ = value;
|
||||
float default_value_;
|
||||
T value_ = value;
|
||||
T default_value_;
|
||||
if (use_inch) {
|
||||
// calc value in inch
|
||||
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 &value,
|
||||
const bool *default_value,
|
||||
const std::string &undo_tooltip)
|
||||
const std::string &undo_tooltip) const
|
||||
{
|
||||
// draw offseted input
|
||||
auto draw_offseted_input = [&]() -> bool {
|
||||
ImGui::SameLine(m_gui_cfg->advanced_input_offset);
|
||||
auto draw_offseted_input = [&offset = m_gui_cfg->advanced_input_offset, &name, &value](){
|
||||
ImGui::SameLine(offset);
|
||||
return ImGui::Checkbox(("##" + name).c_str(), &value);
|
||||
};
|
||||
float undo_offset = ImGui::GetStyle().FramePadding.x;
|
||||
@ -2417,28 +2265,22 @@ void GLGizmoEmboss::draw_height(bool use_inch)
|
||||
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
|
||||
apply(value, limits.emboss);
|
||||
|
||||
// only different value need process
|
||||
return !is_approx(value, m_volume->text_configuration->style.prop.emboss);
|
||||
}
|
||||
|
||||
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())
|
||||
if(!is_approx(value, m_volume->emboss_shape->projection.depth))
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
bool GLGizmoEmboss::rev_slider(const std::string &name,
|
||||
@ -2448,7 +2290,7 @@ bool GLGizmoEmboss::rev_slider(const std::string &name,
|
||||
int v_min,
|
||||
int v_max,
|
||||
const std::string& format,
|
||||
const wxString &tooltip)
|
||||
const wxString &tooltip) const
|
||||
{
|
||||
auto draw_slider_optional_int = [&]() -> bool {
|
||||
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_max,
|
||||
const std::string& format,
|
||||
const wxString &tooltip)
|
||||
const wxString &tooltip) const
|
||||
{
|
||||
auto draw_slider_optional_float = [&]() -> bool {
|
||||
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_max,
|
||||
const std::string &format,
|
||||
const wxString &tooltip)
|
||||
const wxString &tooltip) const
|
||||
{
|
||||
auto draw_slider_float = [&]() -> bool {
|
||||
float slider_offset = m_gui_cfg->advanced_input_offset;
|
||||
@ -2780,9 +2622,10 @@ ImVec2 GLGizmoEmboss::get_minimal_window_size() const
|
||||
else
|
||||
res = m_gui_cfg->minimal_window_size_with_collections;
|
||||
|
||||
bool is_object = m_volume->get_object()->volumes.size() == 1;
|
||||
if (!is_object)
|
||||
// Can change type of volume
|
||||
if (!m_volume->is_the_only_one_part())
|
||||
res.y += m_gui_cfg->height_of_volume_type_selector;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3033,18 +2876,17 @@ bool draw_button(const IconManager::VIcons &icons, IconType type, bool disable){
|
||||
get_icon(icons, type, IconState::disabled),
|
||||
disable);}
|
||||
|
||||
TextDataBase::TextDataBase(DataBase &&parent, const FontFileWithCache &font_file, TextConfiguration &&text_configuration)
|
||||
: DataBase(std::move(parent)), font_file(font_file) /* copy */, text_configuration(std::move(text_configuration))
|
||||
TextDataBase::TextDataBase(DataBase &&parent,
|
||||
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 = this->text_configuration.style.prop;
|
||||
EmbossProjection &p = shape.projection;
|
||||
p.depth = fp.emboss;
|
||||
p.use_surface = fp.use_surface;
|
||||
|
||||
const FontFile &ff = *this->font_file.font_file;
|
||||
const FontProp &fp = m_text_configuration.style.prop;
|
||||
const FontFile &ff = *m_font_file.font_file;
|
||||
shape.scale = get_text_shape_scale(fp, ff);
|
||||
}
|
||||
|
||||
@ -3054,36 +2896,37 @@ EmbossShape &TextDataBase::create_shape()
|
||||
return shape;
|
||||
|
||||
// create shape by configuration
|
||||
const char *text = text_configuration.text.c_str();
|
||||
const FontProp &fp = text_configuration.style.prop;
|
||||
auto was_canceled = [&c = cancel]() -> bool { return c->load(); };
|
||||
shape.shapes = text2shapes(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;
|
||||
|
||||
const char *text = m_text_configuration.text.c_str();
|
||||
const FontProp &fp = m_text_configuration.style.prop;
|
||||
auto was_canceled = [&c = cancel](){ return c->load(); };
|
||||
shape.shapes = text2shapes(m_font_file, text, fp, was_canceled);
|
||||
return shape;
|
||||
}
|
||||
|
||||
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);
|
||||
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)
|
||||
@ -3099,11 +2942,11 @@ std::unique_ptr<DataBase> create_emboss_data_base(const std::string &text, Style
|
||||
style_manager.load_valid_style();
|
||||
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
|
||||
// volume must store valid path
|
||||
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
|
||||
// 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);
|
||||
DataBase base(volume_name, cancel);
|
||||
FontFileWithCache &font = style_manager.get_font_file_with_cache();
|
||||
TextConfiguration tc{es, text};
|
||||
return std::make_unique<TextDataBase>(std::move(base), font, std::move(tc));
|
||||
TextConfiguration tc{static_cast<EmbossStyle>(style), text};
|
||||
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)
|
||||
@ -3163,7 +3006,7 @@ bool store(const Facenames &facenames) {
|
||||
::cereal::BinaryOutputArchive archive(file);
|
||||
std::vector<wxString> good;
|
||||
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};
|
||||
|
||||
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)
|
||||
{
|
||||
for (::FaceName &face : face_names.faces) {
|
||||
for (FaceName &face : face_names.faces) {
|
||||
std::string name_str(face.wx_name.ToUTF8().data());
|
||||
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;
|
||||
|
||||
// set previous cach as deleted
|
||||
for (::FaceName &f : faces.faces)
|
||||
for (FaceName &f : faces.faces)
|
||||
if (f.texture_index == texture_index) {
|
||||
if (f.cancel != nullptr) f.cancel->store(true);
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
bool set_height();
|
||||
// call after set m_style_manager.get_style().prop.emboss
|
||||
bool set_depth();
|
||||
|
||||
bool draw_italic_button();
|
||||
bool draw_bold_button();
|
||||
void draw_advanced();
|
||||
|
||||
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,
|
||||
bool use_inch, const std::optional<float>& scale);
|
||||
|
||||
template<typename T> bool rev_input_mm(const std::string &name, T &value, const T *default_value,
|
||||
const std::string &undo_tooltip, T step, T step_fast, const char *format, bool use_inch, const std::optional<float>& scale) const;
|
||||
|
||||
/// <summary>
|
||||
/// Reversible input float with option to restor default value
|
||||
/// TODO: make more general, static and move to ImGuiWrapper
|
||||
/// </summary>
|
||||
/// <returns>True when value changed otherwise FALSE.</returns>
|
||||
bool rev_input(const std::string &name, float &value, const float *default_value,
|
||||
const std::string &undo_tooltip, float step, float step_fast, const char *format,
|
||||
ImGuiInputTextFlags flags = 0);
|
||||
bool rev_checkbox(const std::string &name, bool &value, const bool* default_value, const std::string &undo_tooltip);
|
||||
template<typename T> bool rev_input(const std::string &name, T &value, const T *default_value,
|
||||
const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags = 0) const;
|
||||
bool rev_checkbox(const std::string &name, bool &value, const bool* default_value, const std::string &undo_tooltip) const;
|
||||
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,
|
||||
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,
|
||||
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip);
|
||||
template<typename T, typename Draw>
|
||||
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 v_min, float v_max, const std::string &format, const wxString &tooltip) const;
|
||||
template<typename T, typename Draw> bool revertible(const std::string &name, T &value, const T *default_value,
|
||||
const std::string &undo_tooltip, float undo_offset, Draw draw) const;
|
||||
|
||||
bool m_should_set_minimal_windows_size = false;
|
||||
void set_minimal_window_size(bool is_advance_edit_style);
|
||||
@ -151,7 +148,6 @@ private:
|
||||
|
||||
struct GuiCfg;
|
||||
std::unique_ptr<const GuiCfg> m_gui_cfg = nullptr;
|
||||
static GuiCfg create_gui_configuration();
|
||||
|
||||
// Is open tree with advanced options
|
||||
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
|
||||
bed_coor = bed.centroid().cast<double>();
|
||||
|
||||
// TODO: need TextConfiguration refactor to work !!!
|
||||
double z = m_input.base->shape.projection.depth / 2;
|
||||
|
||||
Vec3d offset(bed_coor.x(), bed_coor.y(), z);
|
||||
offset -= m_result.center();
|
||||
Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z());
|
||||
|
@ -7,6 +7,12 @@
|
||||
#include "slic3r/GUI/CameraUtils.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 {
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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 {
|
||||
Transform3d to_world = instrance_tr * volume_tr;
|
||||
|
||||
// Use fix of .3mf loaded tranformation when exist
|
||||
if (volume.text_configuration->fix_3mf_tr.has_value())
|
||||
to_world = to_world * (*volume.text_configuration->fix_3mf_tr);
|
||||
if (std::optional<Transform3d> fix = volume.emboss_shape->fix_3mf_tr;
|
||||
fix.has_value())
|
||||
to_world = to_world * (*fix);
|
||||
// zero point of volume in world coordinate system
|
||||
Vec3d volume_center = to_world.translation();
|
||||
// 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();
|
||||
|
||||
if (volume->text_configuration.has_value()) {
|
||||
const TextConfiguration &tc = *volume->text_configuration;
|
||||
// fix baked transformation from .3mf store process
|
||||
if (tc.fix_3mf_tr.has_value())
|
||||
volume_tr = volume_tr * tc.fix_3mf_tr->inverse();
|
||||
if (const std::optional<EmbossShape> &es_opt = volume->emboss_shape;
|
||||
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();
|
||||
@ -165,7 +173,11 @@ 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_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
|
||||
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());
|
||||
|
||||
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
|
||||
if (tc.fix_3mf_tr.has_value())
|
||||
volume_new = volume_new * (*tc.fix_3mf_tr);
|
||||
if (volume != nullptr && volume->emboss_shape.has_value()) {
|
||||
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
|
||||
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
|
||||
@ -317,6 +329,67 @@ std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastMana
|
||||
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 res = gl_volume.world_matrix();
|
||||
@ -325,11 +398,11 @@ Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs
|
||||
if (!mv)
|
||||
return res;
|
||||
|
||||
const std::optional<TextConfiguration> &tc = mv->text_configuration;
|
||||
if (!tc.has_value())
|
||||
const std::optional<EmbossShape> &es = mv->emboss_shape;
|
||||
if (!es.has_value())
|
||||
return res;
|
||||
|
||||
const std::optional<Transform3d> &fix = tc->fix_3mf_tr;
|
||||
const std::optional<Transform3d> &fix = es->fix_3mf_tr;
|
||||
if (!fix.has_value())
|
||||
return res;
|
||||
|
||||
|
@ -37,6 +37,9 @@ struct SurfaceDrag
|
||||
// initial rotation in Z axis of volume
|
||||
std::optional<float> start_angle;
|
||||
|
||||
// initial Z distance from surface
|
||||
std::optional<float> start_distance;
|
||||
|
||||
// Flag whether coordinate hit some volume
|
||||
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>
|
||||
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>
|
||||
/// Get transformation to world
|
||||
/// - use fix after store to 3mf when exists
|
||||
|
Loading…
x
Reference in New Issue
Block a user