Add new volume as cut surface by definition in style

This commit is contained in:
Filip Sykala - NTB T15p 2022-11-21 10:15:20 +01:00
parent c052ff403a
commit 58592593af
12 changed files with 576 additions and 457 deletions

View File

@ -20,9 +20,6 @@
#include "libslic3r/Line.hpp"
using namespace Slic3r;
double Emboss::SHAPE_SCALE = 0.001;//SCALING_FACTOR;
// do not expose out of this file stbtt_ data types
namespace priv{

View File

@ -16,23 +16,21 @@ namespace Slic3r {
/// class with only static function add ability to engraved OR raised
/// text OR polygons onto model surface
/// </summary>
class Emboss
namespace Emboss
{
public:
Emboss() = delete;
// every glyph's shape point is divided by SHAPE_SCALE - increase precission of fixed point value
static double SHAPE_SCALE;
// stored in fonts (to be able represents curve by sequence of lines)
static constexpr double SHAPE_SCALE = 0.001; // SCALING_FACTOR promile is fine enough
/// <summary>
/// Collect fonts registred inside OS
/// </summary>
/// <returns>OS registred TTF font files(full path) with names</returns>
static EmbossStyles get_font_list();
EmbossStyles get_font_list();
#ifdef _WIN32
static EmbossStyles get_font_list_by_register();
static EmbossStyles get_font_list_by_enumeration();
static EmbossStyles get_font_list_by_folder();
EmbossStyles get_font_list_by_register();
EmbossStyles get_font_list_by_enumeration();
EmbossStyles get_font_list_by_folder();
#endif
/// <summary>
@ -40,7 +38,7 @@ public:
/// </summary>
/// <param name="font_face_name">Unique identificator for font</param>
/// <returns>File path to font when found</returns>
static std::optional<std::wstring> get_font_path(const std::wstring &font_face_name);
std::optional<std::wstring> get_font_path(const std::wstring &font_face_name);
// description of one letter
struct Glyph
@ -126,14 +124,14 @@ public:
/// </summary>
/// <param name="file_path">Location of .ttf or .ttc font file</param>
/// <returns>Font object when loaded.</returns>
static std::unique_ptr<FontFile> create_font_file(const char *file_path);
std::unique_ptr<FontFile> create_font_file(const char *file_path);
// data = raw file data
static std::unique_ptr<FontFile> create_font_file(std::unique_ptr<std::vector<unsigned char>> data);
std::unique_ptr<FontFile> create_font_file(std::unique_ptr<std::vector<unsigned char>> data);
#ifdef _WIN32
// fix for unknown pointer HFONT
using HFONT = void*;
static void * can_load(HFONT hfont);
static std::unique_ptr<FontFile> create_font_file(HFONT hfont);
void * can_load(HFONT hfont);
std::unique_ptr<FontFile> create_font_file(HFONT hfont);
#endif // _WIN32
/// <summary>
@ -144,7 +142,7 @@ public:
/// <param name="letter">One character defined by unicode codepoint</param>
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
/// <returns>inner polygon cw(outer ccw)</returns>
static std::optional<Glyph> letter2glyph(const FontFile &font, unsigned int font_index, int letter, float flatness);
std::optional<Glyph> letter2glyph(const FontFile &font, unsigned int font_index, int letter, float flatness);
/// <summary>
/// Convert text into polygons
@ -154,14 +152,14 @@ public:
/// <param name="font_prop">User defined property of the font</param>
/// <param name="was_canceled">Way to interupt processing</param>
/// <returns>Inner polygon cw(outer ccw)</returns>
static ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop, std::function<bool()> was_canceled = nullptr);
ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop, std::function<bool()> was_canceled = nullptr);
/// <summary>
/// Fix intersections and self intersections in polygons glyph shape
/// </summary>
/// <param name="shape">Input shape to heal</param>
/// <returns>Healed shapes</returns>
static ExPolygons heal_shape(const Polygons &shape);
ExPolygons heal_shape(const Polygons &shape);
/// <summary>
/// NOTE: call Slic3r::union_ex before this call
@ -175,7 +173,7 @@ public:
/// <param name="max_iteration">Heal could create another issue,
/// After healing it is checked again until shape is good or maximal count of iteration</param>
/// <returns>True when shapes is good otherwise False</returns>
static bool heal_shape(ExPolygons &shape, unsigned max_iteration = 10);
bool heal_shape(ExPolygons &shape, unsigned max_iteration = 10);
/// <summary>
/// Divide line segments in place near to point
@ -186,7 +184,7 @@ public:
/// <param name="expolygons">Expolygon to edit</param>
/// <param name="distance">(epsilon)Euclidean distance from point to line which divide line</param>
/// <returns>True when some division was made otherwise false</returns>
static bool divide_segments_for_close_point(ExPolygons &expolygons, double distance);
bool divide_segments_for_close_point(ExPolygons &expolygons, double distance);
/// <summary>
/// Use data from font property to modify transformation
@ -194,7 +192,7 @@ public:
/// <param name="font_prop">Z-move as surface distance(FontProp::distance)
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
/// <param name="transformation">In / Out transformation to modify by property</param>
static void apply_transformation(const FontProp &font_prop, Transform3d &transformation);
void apply_transformation(const FontProp &font_prop, Transform3d &transformation);
/// <summary>
/// Read information from naming table of font file
@ -203,7 +201,7 @@ public:
/// <param name="font">Selector of font</param>
/// <param name="font_index">Index of font in collection</param>
/// <returns>True when the font description contains italic/obligue otherwise False</returns>
static bool is_italic(const FontFile &font, unsigned int font_index);
bool is_italic(const FontFile &font, unsigned int font_index);
/// <summary>
/// Create unique character set from string with filtered from text with only character from font
@ -213,7 +211,7 @@ public:
/// <param name="font_index">Define font in collection</param>
/// <param name="exist_unknown">True when text contain glyph unknown in font</param>
/// <returns>Unique set of character from text contained in font</returns>
static std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr);
std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr);
/// <summary>
/// Calculate scale for glyph shape convert from shape points to mm
@ -221,7 +219,7 @@ public:
/// <param name="fp">Property of font</param>
/// <param name="ff">Font data</param>
/// <returns>Conversion to mm</returns>
static double get_shape_scale(const FontProp &fp, const FontFile &ff);
double get_shape_scale(const FontProp &fp, const FontFile &ff);
/// <summary>
/// Project spatial point
@ -274,7 +272,7 @@ public:
/// <param name="shape2d">text or image</param>
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
/// <returns>Projected shape into space</returns>
static indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
/// <summary>
/// Create transformation for emboss text object to lay on surface point
@ -283,7 +281,7 @@ public:
/// <param name="normal">Normal of surface point</param>
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
/// <returns>Transformation onto surface point</returns>
static Transform3d create_transformation_onto_surface(
Transform3d create_transformation_onto_surface(
const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f);
class ProjectZ : public IProjection
@ -345,7 +343,7 @@ public:
Vec3d project(const Vec3d &point) const override;
std::optional<Vec2d> unproject(const Vec3d &p, double * depth = nullptr) const override;
};
};
} // namespace Emboss
} // namespace Slic3r
#endif // slic3r_Emboss_hpp_

View File

@ -936,8 +936,7 @@ private:
// 1 -> is splittable
mutable int m_is_splittable{ -1 };
ModelVolume(ModelObject *object, const TriangleMesh &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) : m_mesh(new TriangleMesh(mesh)), m_type(type), object(object)
{
inline bool check() {
assert(this->id().valid());
assert(this->config.id().valid());
assert(this->supported_facets.id().valid());
@ -947,20 +946,24 @@ private:
assert(this->id() != this->supported_facets.id());
assert(this->id() != this->seam_facets.id());
assert(this->id() != this->mmu_segmentation_facets.id());
if (mesh.facets_count() > 1)
calculate_convex_hull();
return true;
}
ModelVolume(ModelObject *object, const TriangleMesh &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
m_mesh(new TriangleMesh(mesh)), m_type(type), object(object)
{
assert(check());
if (m_mesh->facets_count() > 1) calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART)
: m_mesh(new TriangleMesh(std::move(mesh))), m_type(type), object(object)
{
assert(check());
if (m_mesh->facets_count() > 1) calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) {
assert(this->id().valid());
assert(this->config.id().valid());
assert(this->supported_facets.id().valid());
assert(this->seam_facets.id().valid());
assert(this->mmu_segmentation_facets.id().valid());
assert(this->id() != this->config.id());
assert(this->id() != this->supported_facets.id());
assert(this->id() != this->seam_facets.id());
assert(this->id() != this->mmu_segmentation_facets.id());
assert(check());
}
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.

View File

@ -57,7 +57,9 @@
#endif // ALLOW_DEBUG_MODE
using namespace Slic3r;
using namespace Slic3r::Emboss;
using namespace Slic3r::GUI;
using namespace Slic3r::GUI::Emboss;
// anonymous namespace for unique names
namespace {
@ -165,6 +167,18 @@ void GLGizmoEmboss::set_fine_position()
ImGuiWrapper::draw(rect);
}
namespace priv {
/// <summary>
/// Prepare data for emboss
/// </summary>
/// <param name="text">Text to emboss</param>
/// <param name="style_manager">Keep actual selected style</param>
/// <returns>Base data for emboss text</returns>
static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager);
} // namespace priv
void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
{
assert(volume_type == ModelVolumeType::MODEL_PART ||
@ -188,11 +202,17 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
Plater* plater = wxGetApp().plater();
const Camera &camera = plater->get_camera();
const Pointfs &bed_shape = plater->build_volume().bed_shape();
EmbossDataCreateObject data{create_emboss_data_base(),
screen_coor,
camera,
bed_shape};
auto job = std::make_unique<EmbossCreateObjectJob>(std::move(data));
// TODO: Fix double creation of base data (first is inside function start_volume_creation)
DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager);
FontProp& prop = emboss_data.text_configuration.style.prop;
// can't create new object with using surface
if (prop.use_surface) prop.use_surface = false;
// can't create new object with distance from surface
if (prop.distance.has_value()) prop.distance.reset();
DataCreateObject data{std::move(emboss_data), screen_coor, camera, bed_shape};
auto job = std::make_unique<CreateObjectJob>(std::move(data));
Worker &worker = plater->get_ui_job_worker();
queue_job(worker, std::move(job));
}
@ -369,9 +389,9 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
// Calculate temporary position
Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key);
Transform3d trmat = Emboss::create_transformation_onto_surface(hit->position, hit->normal);
Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal);
const FontProp& font_prop = tc.style.prop;
Emboss::apply_transformation(font_prop, trmat);
apply_transformation(font_prop, trmat);
// fix baked transformation from .3mf store process
if (tc.fix_3mf_tr.has_value())
@ -807,8 +827,7 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); }
bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type,
const Vec2d &screen_coor)
bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type, const Vec2d &screen_coor)
{
Plater* plater = wxGetApp().plater();
@ -848,18 +867,33 @@ bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type,
GLVolume *gl_volume = volumes[hovered_id];
Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix();
// create volume
EmbossDataCreateVolume data{create_emboss_data_base(),
volume_type,
screen_coor,
object_idx_signed,
camera,
*hit,
hit_object_trmat,
hit_instance_trmat};
DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager);
// Create result volume transformation
Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal);
const FontProp& font_prop = emboss_data.text_configuration.style.prop;
apply_transformation(font_prop, surface_trmat);
Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat;
std::unique_ptr<GUI::Job> job;
if (!emboss_data.text_configuration.style.prop.use_surface) {
// create volume
DataCreateVolume data{std::move(emboss_data), volume_type, object_idx_signed, volume_trmat};
job = std::make_unique<CreateVolumeJob>(std::move(data));
} else {
// Model to cut surface from.
SurfaceVolumeData::ModelSources sources = create_sources(obj->volumes);
if (sources.empty()) return false;
bool is_outside = volume_type == ModelVolumeType::MODEL_PART;
// check that there is not unexpected volume type
assert(is_outside ||
volume_type == ModelVolumeType::NEGATIVE_VOLUME ||
volume_type == ModelVolumeType::PARAMETER_MODIFIER);
CreateSurfaceVolumeData surface_data{std::move(emboss_data), volume_trmat, is_outside, std::move(sources), volume_type, object_idx_signed};
job = std::make_unique<CreateSurfaceVolumeJob>(std::move(surface_data));
}
Worker &worker = plater->get_ui_job_worker();
auto job = std::make_unique<EmbossCreateVolumeJob>(std::move(data));
queue_job(worker, std::move(job));
return true;
}
@ -957,7 +991,7 @@ bool GLGizmoEmboss::process()
m_update_job_cancel->store(true);
// create new shared ptr to cancel new job
m_update_job_cancel = std::make_shared<std::atomic<bool> >(false);
EmbossDataUpdate data{create_emboss_data_base(), m_volume->id(), m_update_job_cancel};
DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager), m_volume->id(), m_update_job_cancel};
std::unique_ptr<Job> job = nullptr;
@ -965,7 +999,7 @@ bool GLGizmoEmboss::process()
const TextConfiguration &tc = data.text_configuration;
if (tc.style.prop.use_surface) {
// Model to cut surface from.
UseSurfaceData::ModelSources sources = UseSurfaceData::create_sources(m_volume);
SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume);
if (sources.empty()) return false;
Transform3d text_tr = m_volume->get_matrix();
@ -977,11 +1011,10 @@ bool GLGizmoEmboss::process()
// check that there is not unexpected volume type
assert(is_outside || m_volume->is_negative_volume() ||
m_volume->is_modifier());
UseSurfaceData surface_data{std::move(data), text_tr, is_outside,
std::move(sources)};
job = std::make_unique<UseSurfaceJob>(std::move(surface_data));
UpdateSurfaceVolumeData surface_data{std::move(data), text_tr, is_outside, std::move(sources)};
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
} else {
job = std::make_unique<EmbossUpdateJob>(std::move(data));
job = std::make_unique<UpdateJob>(std::move(data));
}
//*
@ -1024,7 +1057,7 @@ void GLGizmoEmboss::close()
void GLGizmoEmboss::discard_and_close() {
if (!m_unmodified_volume.has_value()) return;
m_volume->set_transformation(m_unmodified_volume->tr);
EmbossUpdateJob::update_volume(m_volume, std::move(m_unmodified_volume->tm), m_unmodified_volume->tc, m_unmodified_volume->name);
UpdateJob::update_volume(m_volume, std::move(m_unmodified_volume->tm), m_unmodified_volume->tc, m_unmodified_volume->name);
close();
//auto plater = wxGetApp().plater();
@ -1139,18 +1172,18 @@ void GLGizmoEmboss::draw_window()
void GLGizmoEmboss::draw_text_input()
{
auto create_range_text = [&mng = m_style_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph]() {
auto create_range_text_prep = [&mng = m_style_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph]() {
auto& ff = mng.get_font_file_with_cache();
assert(ff.has_value());
const auto &cn = mng.get_font_prop().collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
return Emboss::create_range_text(text, *ff.font_file, font_index, &exist_unknown);
return create_range_text(text, *ff.font_file, font_index, &exist_unknown);
};
ImFont *imgui_font = m_style_manager.get_imgui_font();
if (imgui_font == nullptr) {
// try create new imgui font
m_style_manager.create_imgui_font(create_range_text());
m_style_manager.create_imgui_font(create_range_text_prep());
imgui_font = m_style_manager.get_imgui_font();
}
bool exist_font =
@ -1190,10 +1223,10 @@ void GLGizmoEmboss::draw_text_input()
if (prop.line_gap.has_value())
append_warning(_u8L("Line gap"), _u8L("Unsupported visualization of gap between lines inside text input."));
auto &ff = m_style_manager.get_font_file_with_cache();
float imgui_size = EmbossStyleManager::get_imgui_font_size(prop, *ff.font_file);
if (imgui_size > EmbossStyleManager::max_imgui_font_size)
float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file);
if (imgui_size > StyleManager::max_imgui_font_size)
append_warning(_u8L("To tall"), _u8L("Diminished font height inside text input."));
if (imgui_size < EmbossStyleManager::min_imgui_font_size)
if (imgui_size < StyleManager::min_imgui_font_size)
append_warning(_u8L("To small"), _u8L("Enlarged font height inside text input."));
if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who);
}
@ -1217,7 +1250,7 @@ void GLGizmoEmboss::draw_text_input()
const ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll;
if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) {
process();
range_text = create_range_text();
range_text = create_range_text_prep();
}
if (exist_font) ImGui::PopFont();
@ -1963,7 +1996,7 @@ void GLGizmoEmboss::draw_delete_style_button() {
void GLGizmoEmboss::draw_revert_all_styles_button() {
if (draw_button(IconType::revert_all)) {
m_style_manager = EmbossStyleManager(m_imgui->get_glyph_ranges());
m_style_manager = StyleManager(m_imgui->get_glyph_ranges());
m_style_manager.init(nullptr, create_default_styles());
assert(m_style_manager.get_font_file_with_cache().has_value());
process();
@ -2029,7 +2062,7 @@ void GLGizmoEmboss::draw_style_list() {
m_style_manager.init_style_images(m_gui_cfg->max_style_image_size, m_text);
m_style_manager.init_trunc_names(max_style_name_width);
std::optional<std::pair<size_t,size_t>> swap_indexes;
const std::vector<EmbossStyleManager::Item> &styles = m_style_manager.get_styles();
const std::vector<StyleManager::Item> &styles = m_style_manager.get_styles();
for (const auto &item : styles) {
size_t index = &item - &styles.front();
const EmbossStyle &style = item.style;
@ -2038,7 +2071,7 @@ void GLGizmoEmboss::draw_style_list() {
bool is_selected = (index == m_style_manager.get_style_index());
ImVec2 select_size(0,m_gui_cfg->max_style_image_size.y()); // 0,0 --> calculate in draw
const std::optional<EmbossStyleManager::StyleImage> &img = item.image;
const std::optional<StyleManager::StyleImage> &img = item.image;
// allow click delete button
ImGuiSelectableFlags_ flags = ImGuiSelectableFlags_AllowItemOverlap;
if (ImGui::Selectable(item.truncated_name.c_str(), is_selected, flags, select_size)) {
@ -2918,9 +2951,9 @@ bool GLGizmoEmboss::choose_svg_file()
for (const auto &p : polys) bb.merge(p.contour.points);
const FontProp &fp = m_style_manager.get_style().prop;
float scale = fp.size_in_mm / std::max(bb.max.x(), bb.max.y());
auto project = std::make_unique<Emboss::ProjectScale>(
std::make_unique<Emboss::ProjectZ>(fp.emboss / scale), scale);
indexed_triangle_set its = Emboss::polygons2model(polys, *project);
auto project = std::make_unique<ProjectScale>(
std::make_unique<ProjectZ>(fp.emboss / scale), scale);
indexed_triangle_set its = polygons2model(polys, *project);
return false;
// test store:
// for (auto &poly : polys) poly.scale(1e5);
@ -2929,41 +2962,38 @@ bool GLGizmoEmboss::choose_svg_file()
//return add_volume(name, its);
}
EmbossDataBase GLGizmoEmboss::create_emboss_data_base() {
DataBase priv::create_emboss_data_base(const std::string &text, StyleManager& style_manager)
{
auto create_volume_name = [&]() {
bool contain_enter = m_text.find('\n') != std::string::npos;
bool contain_enter = text.find('\n') != std::string::npos;
std::string text_fixed;
if (contain_enter) {
// change enters to space
text_fixed = m_text; // copy
text_fixed = text; // copy
std::replace(text_fixed.begin(), text_fixed.end(), '\n', ' ');
}
return _u8L("Text") + " - " + ((contain_enter) ? text_fixed : m_text);
return _u8L("Text") + " - " + ((contain_enter) ? text_fixed : text);
};
auto create_configuration = [&]() -> TextConfiguration {
if (!m_style_manager.is_activ_font()) {
if (!style_manager.is_activ_font()) {
std::string default_text_for_emboss = _u8L("Embossed text");
EmbossStyle es = m_style_manager.get_style();
EmbossStyle es = style_manager.get_style();
TextConfiguration tc{es, default_text_for_emboss};
// TODO: investigate how to initialize
return tc;
}
EmbossStyle &es = m_style_manager.get_style();
EmbossStyle &es = style_manager.get_style();
// actualize font path - during changes in gui it could be corrupted
// volume must store valid path
assert(m_style_manager.get_wx_font().has_value());
assert(es.path.compare(WxFontUtils::store_wxFont(*m_style_manager.get_wx_font())) == 0);
assert(style_manager.get_wx_font().has_value());
assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0);
// style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font());
return TextConfiguration{es, m_text};
return TextConfiguration{es, text};
};
return EmbossDataBase{
m_style_manager.get_font_file_with_cache(),
create_configuration(),
create_volume_name()
};
return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), create_configuration(), create_volume_name()};
}
bool GLGizmoEmboss::load_configuration(ModelVolume *volume)
@ -2974,7 +3004,7 @@ bool GLGizmoEmboss::load_configuration(ModelVolume *volume)
const TextConfiguration &tc = *tc_opt;
const EmbossStyle &style = tc.style;
auto has_same_name = [&style](const EmbossStyleManager::Item &style_item) -> bool {
auto has_same_name = [&style](const StyleManager::Item &style_item) -> bool {
const EmbossStyle &es = style_item.style;
return es.name == style.name;
};

View File

@ -34,8 +34,6 @@ namespace Slic3r{
namespace Slic3r::GUI {
class MeshRaycaster;
struct EmbossDataBase;
class GLGizmoEmboss : public GLGizmoBase
{
public:
@ -157,8 +155,6 @@ private:
bool choose_true_type_file();
bool choose_svg_file();
// prepare base data for emboss text
EmbossDataBase create_emboss_data_base();
bool load_configuration(ModelVolume *volume);
// When open text loaded from .3mf it could be written with unknown font
@ -225,7 +221,7 @@ private:
std::optional<ImVec2> m_set_window_offset;
bool m_is_advanced_edit_style = false;
EmbossStyleManager m_style_manager;
Emboss::StyleManager m_style_manager;
struct FaceName{
wxString wx_name;

View File

@ -15,11 +15,13 @@
#include "slic3r/GUI/GLCanvas3D.hpp"
using namespace Slic3r;
using namespace Slic3r::Emboss;
using namespace Slic3r::GUI;
using namespace Slic3r::GUI::Emboss;
CreateFontStyleImagesJob::CreateFontStyleImagesJob(
EmbossStyleManager::StyleImagesData &&input)
StyleManager::StyleImagesData &&input)
: m_input(std::move(input))
{
assert(m_input.result != nullptr);
@ -34,15 +36,15 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
// create shapes and calc size (bounding boxes)
std::vector<ExPolygons> name_shapes(m_input.styles.size());
std::vector<double> scales(m_input.styles.size());
images = std::vector<EmbossStyleManager::StyleImage>(m_input.styles.size());
images = std::vector<StyleManager::StyleImage>(m_input.styles.size());
for (auto &item : m_input.styles) {
size_t index = &item - &m_input.styles.front();
ExPolygons &shapes = name_shapes[index];
shapes = Emboss::text2shapes(item.font, m_input.text.c_str(), item.prop);
shapes = text2shapes(item.font, m_input.text.c_str(), item.prop);
// create image description
EmbossStyleManager::StyleImage &image = images[index];
StyleManager::StyleImage &image = images[index];
BoundingBox &bounding_box = image.bounding_box;
for (ExPolygon &shape : shapes)
bounding_box.merge(BoundingBox(shape.contour.points));
@ -56,7 +58,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
const auto &cn = item.prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
double unit_per_em = item.font.font_file->infos[font_index].unit_per_em;
double scale = item.prop.size_in_mm / unit_per_em * Emboss::SHAPE_SCALE * ppm;
double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * ppm;
scales[index] = scale;
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
@ -77,14 +79,14 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
// arrange bounding boxes
int offset_y = 0;
width = 0;
for (EmbossStyleManager::StyleImage &image : images) {
for (StyleManager::StyleImage &image : images) {
image.offset.y() = offset_y;
offset_y += image.tex_size.y+1;
if (width < image.tex_size.x)
width = image.tex_size.x;
}
height = offset_y;
for (EmbossStyleManager::StyleImage &image : images) {
for (StyleManager::StyleImage &image : images) {
const Point &o = image.offset;
const ImVec2 &s = image.tex_size;
image.uv0 = ImVec2(o.x() / (double) width,
@ -97,7 +99,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
pixels = std::vector<unsigned char>(4*width * height, {255});
// upload sub textures
for (EmbossStyleManager::StyleImage &image : images) {
for (StyleManager::StyleImage &image : images) {
sla::Resolution resolution(image.tex_size.x, image.tex_size.y);
size_t index = &image - &images.front();
double pixel_dim = SCALING_FACTOR / scales[index];
@ -146,7 +148,7 @@ void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &)
// set up texture id
void *texture_id = (void *) (intptr_t) tex_id;
for (EmbossStyleManager::StyleImage &image : images)
for (StyleManager::StyleImage &image : images)
image.texture_id = texture_id;
// move to result

View File

@ -7,7 +7,7 @@
#include "slic3r/Utils/EmbossStyleManager.hpp"
#include "Job.hpp"
namespace Slic3r::GUI {
namespace Slic3r::GUI::Emboss {
/// <summary>
/// Create texture with name of styles written by its style
@ -15,7 +15,7 @@ namespace Slic3r::GUI {
/// </summary>
class CreateFontStyleImagesJob : public Job
{
EmbossStyleManager::StyleImagesData m_input;
StyleManager::StyleImagesData m_input;
// Output data
// texture size
@ -23,10 +23,10 @@ class CreateFontStyleImagesJob : public Job
// texture data
std::vector<unsigned char> pixels;
// descriptors of sub textures
std::vector<EmbossStyleManager::StyleImage> images;
std::vector<StyleManager::StyleImage> images;
public:
CreateFontStyleImagesJob(EmbossStyleManager::StyleImagesData &&input);
CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &) override;
};

View File

@ -19,7 +19,9 @@
#include "slic3r/Utils/UndoRedo.hpp"
using namespace Slic3r;
using namespace GUI;
using namespace Slic3r::Emboss;
using namespace Slic3r::GUI;
using namespace Slic3r::GUI::Emboss;
// private namespace
namespace priv{
@ -31,11 +33,12 @@ constexpr float safe_extension = 1.0f;
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
bool check(const EmbossDataBase &input, bool check_fontfile = true);
bool check(const EmbossDataCreateVolume &input, bool is_main_thread = false);
bool check(const EmbossDataCreateObject &input);
bool check(const EmbossDataUpdate &input, bool is_main_thread = false);
bool check(const UseSurfaceData &input, bool is_main_thread = false);
bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
bool check(const DataCreateVolume &input, bool is_main_thread = false);
bool check(const DataCreateObject &input);
bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
// <summary>
/// Try to create mesh from text
@ -46,14 +49,8 @@ bool check(const UseSurfaceData &input, bool is_main_thread = false);
/// NOTE: Cache glyphs is changed</param>
/// <param name="was_canceled">To check if process was canceled</param>
/// <returns>Triangle mesh model</returns>
template<typename Fnc>
static TriangleMesh try_create_mesh(const EmbossDataBase &input,
Emboss::FontFileWithCache &font,
Fnc was_canceled);
template<typename Fnc>
static TriangleMesh create_mesh(EmbossDataBase &input,
Fnc was_canceled,
Job::Ctl &ctl);
template<typename Fnc> static TriangleMesh try_create_mesh(const DataBase &input, FontFileWithCache &font, Fnc was_canceled);
template<typename Fnc> static TriangleMesh create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl &ctl);
/// <summary>
/// Create default mesh for embossed text
@ -66,7 +63,18 @@ static TriangleMesh create_default_mesh();
/// </summary>
/// <param name="mesh">New mesh data</param>
/// <param name="data">Text configuration, ...</param>
static void update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data);
static void update_volume(TriangleMesh &&mesh, const DataUpdate &data);
/// <summary>
/// Add new volume to object
/// </summary>
/// <param name="mesh">triangles of new volume</param>
/// <param name="object_idx">Object where to add volume</param>
/// <param name="type">Type of new volume</param>
/// <param name="trmat">Transformation of volume inside of object</param>
/// <param name="data">Text configuration and New VolumeName</param>
static void create_volume(TriangleMesh &&mesh, const size_t object_idx,
const ModelVolumeType type, const Transform3d trmat, const DataBase &data);
/// <summary>
/// Select Volume from objects
@ -74,8 +82,7 @@ static void update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data);
/// <param name="objects">All objects in scene</param>
/// <param name="volume_id">Identifier of volume in object</param>
/// <returns>Pointer to volume when exist otherwise nullptr</returns>
static ModelVolume *get_volume(ModelObjectPtrs &objects,
const ObjectID &volume_id);
static ModelVolume *get_volume(ModelObjectPtrs &objects, const ObjectID &volume_id);
/// <summary>
/// Create projection for cut surface from mesh
@ -84,10 +91,7 @@ static ModelVolume *get_volume(ModelObjectPtrs &objects,
/// <param name="shape_scale">Convert shape to milimeters</param>
/// <param name="z_range">Bounding box 3d of model volume for projection ranges</param>
/// <returns>Orthogonal cut_projection</returns>
static Emboss::OrthoProject create_projection_for_cut(
Transform3d tr,
double shape_scale,
const std::pair<float, float> &z_range);
static OrthoProject create_projection_for_cut(Transform3d tr, double shape_scale, const std::pair<float, float> &z_range);
/// <summary>
/// Create tranformation for emboss Cutted surface
@ -97,41 +101,43 @@ static Emboss::OrthoProject create_projection_for_cut(
/// <param name="tr">Text voliume transformation inside object</param>
/// <param name="cut">Cutted surface from model</param>
/// <returns>Projection</returns>
static Emboss::OrthoProject3d create_emboss_projection(
bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut);
static OrthoProject3d create_emboss_projection(bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut);
/// <summary>
/// Cut surface into triangle mesh
/// </summary>
/// <param name="input1">(can't be const - cache of font)</param>
/// <param name="input2">SurfaceVolume data</param>
/// <param name="was_canceled">Check to interupt execution</param>
/// <returns>Extruded object from cuted surace</returns>
static TriangleMesh cut_surface(/*const*/ DataBase &input1, const SurfaceVolumeData &input2, std::function<bool()> was_canceled);
static void create_message(const std::string &message); // only in finalize
static bool process(std::exception_ptr &eptr);
class EmbossJobException : public std::runtime_error
{ public: EmbossJobException(const char* message):runtime_error(message){}};
class JobException : public std::runtime_error {
public: JobException(const char* message):runtime_error(message){}};
}// namespace priv
/////////////////
/// Create Volume
EmbossCreateVolumeJob::EmbossCreateVolumeJob(EmbossDataCreateVolume &&input)
CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input)
: m_input(std::move(input))
{
assert(priv::check(m_input, true));
}
void EmbossCreateVolumeJob::process(Ctl &ctl) {
void CreateVolumeJob::process(Ctl &ctl) {
if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossCreateVolumeJob.");
auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); };
m_result = priv::create_mesh(m_input, was_canceled, ctl);
if (was_canceled()) return;
// Create new volume inside of object
const FontProp &font_prop = m_input.text_configuration.style.prop;
Transform3d surface_trmat = Emboss::create_transformation_onto_surface(
m_input.hit.position, m_input.hit.normal);
Emboss::apply_transformation(font_prop, surface_trmat);
m_transformation = m_input.hit_instance_tr.inverse() *
m_input.hit_object_tr * surface_trmat;
// center result
Vec3f c = m_result.bounding_box().center().cast<float>();
if (!c.isApprox(Vec3f::Zero())) m_result.translate(-c);
}
void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
// doesn't care about exception when process was canceled by user
if (canceled) {
eptr = nullptr;
@ -141,74 +147,19 @@ void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
if (m_result.its.empty())
return priv::create_message(_u8L("Can't create empty volume."));
GUI_App &app = wxGetApp();
Plater *plater = app.plater();
ObjectList *obj_list = app.obj_list();
GLCanvas3D *canvas = plater->canvas3D();
ModelObjectPtrs &objects = plater->model().objects;
// create volume in object
size_t object_idx = m_input.object_idx;
// Parent object for text volume was propably removed.
// Assumption: User know what he does, so text volume is no more needed.
if (objects.size() <= object_idx)
return;
plater->take_snapshot(_L("Add Emboss text Volume"));
ModelObject *obj = objects[object_idx];
ModelVolumeType type = m_input.volume_type;
ModelVolume *volume = obj->add_volume(std::move(m_result), type);
// set a default extruder value, since user can't add it manually
volume->config.set_key_value("extruder", new ConfigOptionInt(0));
// do not allow model reload from disk
volume->source.is_from_builtin_objects = true;
volume->name = m_input.volume_name;
volume->text_configuration = std::move(m_input.text_configuration);
volume->set_transformation(m_transformation);
// update volume name in object list
// updata selection after new volume added
// change name of volume in right panel
// select only actual volume
// when new volume is created change selection to this volume
auto add_to_selection = [volume](const ModelVolume *vol) {
return vol == volume;
};
wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(
m_input.object_idx, add_to_selection);
if (!sel.IsEmpty()) obj_list->select_item(sel.front());
// update printable state on canvas
if (type == ModelVolumeType::MODEL_PART)
canvas->update_instance_printable_state_for_object(object_idx);
obj_list->selection_changed();
// Now is valid text volume selected open emboss gizmo
GLGizmosManager &manager = canvas->get_gizmos_manager();
if (manager.get_current_type() != GLGizmosManager::Emboss)
manager.open_gizmo(GLGizmosManager::Emboss);
// redraw scene
canvas->reload_scene(true);
priv::create_volume(std::move(m_result), m_input.object_idx, m_input.volume_type, m_input.trmat, m_input);
}
/////////////////
/// Create Object
EmbossCreateObjectJob::EmbossCreateObjectJob(EmbossDataCreateObject &&input)
CreateObjectJob::CreateObjectJob(DataCreateObject &&input)
: m_input(std::move(input))
{
assert(priv::check(m_input));
}
void EmbossCreateObjectJob::process(Ctl &ctl)
void CreateObjectJob::process(Ctl &ctl)
{
if (!priv::check(m_input))
throw std::runtime_error("Bad input data for EmbossCreateObjectJob.");
@ -240,7 +191,7 @@ void EmbossCreateObjectJob::process(Ctl &ctl)
m_transformation = Transform3d(tt);
}
void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr)
void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr)
{
// doesn't care about exception when process was canceled by user
if (canceled) {
@ -279,13 +230,13 @@ void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr)
/////////////////
/// Update Volume
EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input)
UpdateJob::UpdateJob(DataUpdate&& input)
: m_input(std::move(input))
{
assert(priv::check(m_input, true));
}
void EmbossUpdateJob::process(Ctl &ctl)
void UpdateJob::process(Ctl &ctl)
{
if (!priv::check(m_input))
throw std::runtime_error("Bad input data for EmbossUpdateJob.");
@ -297,16 +248,14 @@ void EmbossUpdateJob::process(Ctl &ctl)
m_result = priv::try_create_mesh(m_input, m_input.font_file, was_canceled);
if (was_canceled()) return;
if (m_result.its.empty())
throw priv::EmbossJobException(
_u8L("Created text volume is empty. Change text or "
"font.").c_str());
throw priv::JobException(_u8L("Created text volume is empty. Change text or font.").c_str());
// center triangle mesh
Vec3d shift = m_result.bounding_box().center();
m_result.translate(-shift.cast<float>());
}
void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &eptr)
void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr)
{
// doesn't care about exception when process was canceled by user
if (canceled || m_input.cancel->load()) {
@ -317,19 +266,14 @@ void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &eptr)
priv::update_volume(std::move(m_result), m_input);
}
UseSurfaceData::ModelSources UseSurfaceData::create_sources(
const ModelVolume *text_volume)
{
if (text_volume == nullptr) return {};
if (!text_volume->text_configuration.has_value()) return {};
const ModelVolumePtrs &volumes = text_volume->get_object()->volumes;
// no other volume in object
if (volumes.size() <= 1) return {};
namespace Slic3r::GUI::Emboss {
ModelSources result;
SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> text_volume_id)
{
SurfaceVolumeData::ModelSources result;
result.reserve(volumes.size() - 1);
for (const ModelVolume *v : volumes) {
if (v->id() == text_volume->id()) continue;
if (text_volume_id.has_value() && v->id().id == *text_volume_id) continue;
// skip modifiers and negative volumes, ...
if (!v->is_model_part()) continue;
const TriangleMesh &tm = v->mesh();
@ -340,15 +284,60 @@ UseSurfaceData::ModelSources UseSurfaceData::create_sources(
return result;
}
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume)
{
if (text_volume == nullptr) return {};
if (!text_volume->text_configuration.has_value()) return {};
const ModelVolumePtrs &volumes = text_volume->get_object()->volumes;
// no other volume in object
if (volumes.size() <= 1) return {};
return create_sources(volumes, text_volume->id().id);
}
} // namespace Slic3r::GUI::Emboss
/////////////////
/// Cut Surface
UseSurfaceJob::UseSurfaceJob(UseSurfaceData &&input)
/// Create Surface volume
CreateSurfaceVolumeJob::CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input)
: m_input(std::move(input))
{
assert(priv::check(m_input, true));
}
void UseSurfaceJob::process(Ctl &ctl) {
void CreateSurfaceVolumeJob::process(Ctl &ctl) {
if (!priv::check(m_input))
throw std::runtime_error("Bad input data for CreateSurfaceVolumeJob.");
// check cancelation of process
auto was_canceled = [&ctl]() -> bool { return ctl.was_canceled(); };
m_result = priv::cut_surface(m_input, m_input, was_canceled);
}
void CreateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
// doesn't care about exception when process was canceled by user
if (canceled) return;
if (priv::process(eptr)) return;
// TODO: Find better way to Not center volume data when add !!!
TriangleMesh mesh = m_result; // Part1: copy
priv::create_volume(std::move(m_result), m_input.object_idx,
m_input.volume_type, m_input.text_tr, m_input);
// Part2: update volume data
//auto vol = wxGetApp().plater()->model().objects[m_input.object_idx]->volumes.back();
//UpdateJob::update_volume(vol, std::move(mesh), m_input.text_configuration, m_input.volume_name);
}
/////////////////
/// Cut Surface
UpdateSurfaceVolumeJob::UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input)
: m_input(std::move(input))
{
assert(priv::check(m_input, true));
}
void UpdateSurfaceVolumeJob::process(Ctl &ctl)
{
if (!priv::check(m_input))
throw std::runtime_error("Bad input data for UseSurfaceJob.");
@ -357,107 +346,25 @@ void UseSurfaceJob::process(Ctl &ctl) {
if (cancel->load()) return true;
return ctl.was_canceled();
};
const TextConfiguration &tc = m_input.text_configuration;
const char *text = tc.text.c_str();
const FontProp &fp = tc.style.prop;
ExPolygons shapes = Emboss::text2shapes(m_input.font_file, text, fp, was_canceled);
if (shapes.empty() || shapes.front().contour.empty())
throw priv::EmbossJobException(
_u8L("Font doesn't have any shape for given text.").c_str());
if (was_canceled()) return;
// Define alignment of text - left, right, center, top bottom, ....
BoundingBox bb = get_extents(shapes);
Point projection_center = bb.center();
for (ExPolygon &shape : shapes) shape.translate(-projection_center);
bb.translate(-projection_center);
const Emboss::FontFile &ff = *m_input.font_file.font_file;
double shape_scale = Emboss::get_shape_scale(fp, ff);
size_t biggest_count = 0;
const UseSurfaceData::ModelSource *biggest = nullptr;
std::vector<size_t> s_to_itss(m_input.sources.size(), std::numeric_limits<size_t>::max());
std::vector<indexed_triangle_set> itss;
itss.reserve(m_input.sources.size());
for (const UseSurfaceData::ModelSource &s : m_input.sources) {
Transform3d mesh_tr_inv = s.tr.inverse();
Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr;
std::pair<float, float> z_range{0., 1.};
Emboss::OrthoProject cut_projection =
priv::create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
// copy only part of source model
indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection);
if (its.indices.empty()) continue;
if (biggest_count < its.vertices.size()) {
biggest_count = its.vertices.size();
biggest = &s;
}
s_to_itss[&s - &m_input.sources.front()] = itss.size();
itss.emplace_back(std::move(its));
}
if (itss.empty())
throw priv::EmbossJobException(_u8L("There is no volume in projection direction.").c_str());
Transform3d tr_inv = biggest->tr.inverse();
size_t itss_index = s_to_itss[biggest - &m_input.sources.front()];
BoundingBoxf3 mesh_bb = bounding_box(itss[itss_index]);
for (const UseSurfaceData::ModelSource &s : m_input.sources) {
if (&s == biggest) continue;
size_t itss_index = s_to_itss[&s - &m_input.sources.front()];
if (itss_index == std::numeric_limits<size_t>::max()) continue;
Transform3d tr = s.tr * tr_inv;
indexed_triangle_set &its = itss[itss_index];
its_transform(its, tr);
BoundingBoxf3 bb = bounding_box(its);
mesh_bb.merge(bb);
m_result = priv::cut_surface(m_input, m_input, was_canceled);
}
Transform3d mesh_tr_inv = tr_inv;
Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr;
Transform3d emboss_tr = cut_projection_tr.inverse();
BoundingBoxf3 mesh_bb_tr = mesh_bb.transformed(emboss_tr);
std::pair<float, float> z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()};
Emboss::OrthoProject cut_projection = priv::create_projection_for_cut(
cut_projection_tr, shape_scale, z_range);
float projection_ratio = (-z_range.first + priv::safe_extension) /
(z_range.second - z_range.first + 2 * priv::safe_extension);
// Use CGAL to cut surface from triangle mesh
SurfaceCut cut = cut_surface(shapes, itss, cut_projection, projection_ratio);
if (cut.empty())
throw priv::EmbossJobException(
_u8L("There is no valid surface for text projection.").c_str());
if (was_canceled()) return;
// !! Projection needs to transform cut
Emboss::OrthoProject3d projection = priv::create_emboss_projection(
m_input.is_outside, fp.emboss, emboss_tr, cut);
indexed_triangle_set new_its = cut2model(cut, projection);
assert(!new_its.empty());
if (was_canceled()) return;
//its_write_obj(new_its, "C:/data/temp/projected.obj"); // only debug
m_result = TriangleMesh(std::move(new_its));
}
void UseSurfaceJob::finalize(bool canceled, std::exception_ptr &eptr)
void UpdateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr)
{
// doesn't care about exception when process was canceled by user
if (canceled || m_input.cancel->load()) {
if (m_input.cancel->load()) {
eptr = nullptr;
return;
}
if (canceled) return;
if (priv::process(eptr)) return;
priv::update_volume(std::move(m_result), m_input);
}
////////////////////////////
/// private namespace implementation
bool priv::check(const EmbossDataBase &input, bool check_fontfile){
bool priv::check(const DataBase &input, bool check_fontfile, bool use_surface)
{
bool res = true;
if (check_fontfile) {
assert(input.font_file.has_value());
@ -469,26 +376,24 @@ bool priv::check(const EmbossDataBase &input, bool check_fontfile){
res &= !input.text_configuration.text.empty();
assert(!input.volume_name.empty());
res &= !input.volume_name.empty();
assert(input.text_configuration.style.prop.use_surface == use_surface);
res &= input.text_configuration.style.prop.use_surface == use_surface;
return res;
}
bool priv::check(const EmbossDataCreateVolume &input, bool is_main_thread) {
bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
bool check_fontfile = false;
bool res = check((EmbossDataBase) input, check_fontfile);
bool res = check((DataBase) input, check_fontfile);
assert(input.volume_type != ModelVolumeType::INVALID);
res &= input.volume_type != ModelVolumeType::INVALID;
assert(input.object_idx >= 0);
res &= input.object_idx >= 0;
if (is_main_thread)
assert((size_t)input.object_idx < wxGetApp().model().objects.size());
assert(input.screen_coor.x() >= 0.);
res &= input.screen_coor.x() >= 0.;
assert(input.screen_coor.y() >= 0.);
res &= input.screen_coor.y() >= 0.;
return res;
}
bool priv::check(const EmbossDataCreateObject &input) {
bool priv::check(const DataCreateObject &input) {
bool check_fontfile = false;
bool res = check((EmbossDataBase) input, check_fontfile);
bool res = check((DataBase) input, check_fontfile);
assert(input.screen_coor.x() >= 0.);
res &= input.screen_coor.x() >= 0.;
assert(input.screen_coor.y() >= 0.);
@ -497,8 +402,9 @@ bool priv::check(const EmbossDataCreateObject &input) {
res &= input.bed_shape.size() >= 3;
return res;
}
bool priv::check(const EmbossDataUpdate &input, bool is_main_thread){
bool res = check((EmbossDataBase) input);
bool priv::check(const DataUpdate &input, bool is_main_thread, bool use_surface){
bool check_fontfile = true;
bool res = check((DataBase) input, check_fontfile, use_surface);
assert(input.volume_id.id >= 0);
res &= input.volume_id.id >= 0;
if (is_main_thread)
@ -509,15 +415,24 @@ bool priv::check(const EmbossDataUpdate &input, bool is_main_thread){
assert(!input.cancel->load());
return res;
}
bool priv::check(const UseSurfaceData &input, bool is_main_thread){
bool res = check((EmbossDataUpdate) input, is_main_thread);
bool priv::check(const CreateSurfaceVolumeData &input, bool is_main_thread)
{
bool use_surface = true;
bool res = check((DataBase)input, is_main_thread, use_surface);
assert(!input.sources.empty());
res &= !input.sources.empty();
return res;
}
bool priv::check(const UpdateSurfaceVolumeData &input, bool is_main_thread){
bool use_surface = true;
bool res = check((DataUpdate)input, is_main_thread, use_surface);
assert(!input.sources.empty());
res &= !input.sources.empty();
return res;
}
template<typename Fnc>
TriangleMesh priv::try_create_mesh(const EmbossDataBase &input, Emboss::FontFileWithCache &font, Fnc was_canceled)
TriangleMesh priv::try_create_mesh(const DataBase &input, FontFileWithCache &font, Fnc was_canceled)
{
const TextConfiguration &tc = input.text_configuration;
const char *text = tc.text.c_str();
@ -526,7 +441,7 @@ TriangleMesh priv::try_create_mesh(const EmbossDataBase &input, Emboss::FontFile
assert(font.has_value());
if (!font.has_value()) return {};
ExPolygons shapes = Emboss::text2shapes(font, text, prop, was_canceled);
ExPolygons shapes = text2shapes(font, text, prop, was_canceled);
if (shapes.empty()) return {};
if (was_canceled()) return {};
@ -536,14 +451,14 @@ TriangleMesh priv::try_create_mesh(const EmbossDataBase &input, Emboss::FontFile
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;
auto projectZ = std::make_unique<Emboss::ProjectZ>(depth);
Emboss::ProjectScale project(std::move(projectZ), scale);
auto projectZ = std::make_unique<ProjectZ>(depth);
ProjectScale project(std::move(projectZ), scale);
if (was_canceled()) return {};
return TriangleMesh(Emboss::polygons2model(shapes, project));
return TriangleMesh(polygons2model(shapes, project));
}
template<typename Fnc>
TriangleMesh priv::create_mesh(EmbossDataBase &input, Fnc was_canceled, Job::Ctl& ctl)
TriangleMesh priv::create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl& ctl)
{
// It is neccessary to create some shape
// Emboss text window is opened by creation new emboss text object
@ -579,7 +494,7 @@ TriangleMesh priv::create_default_mesh()
return triangle_mesh;
}
void EmbossUpdateJob::update_volume(ModelVolume *volume,
void UpdateJob::update_volume(ModelVolume *volume,
TriangleMesh &&mesh,
const TextConfiguration &text_configuration,
const std::string &volume_name)
@ -626,7 +541,7 @@ void EmbossUpdateJob::update_volume(ModelVolume *volume,
canvas->reload_scene(refresh_immediately);
}
void priv::update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data)
void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data)
{
// for sure that some object will be created
if (mesh.its.empty())
@ -652,7 +567,72 @@ void priv::update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data)
if (tc.has_value() && tc->fix_3mf_tr.has_value())
volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse());
EmbossUpdateJob::update_volume(volume, std::move(mesh), data.text_configuration, data.volume_name);
UpdateJob::update_volume(volume, std::move(mesh), data.text_configuration, data.volume_name);
}
void priv::create_volume(
TriangleMesh &&mesh, const size_t object_idx,
const ModelVolumeType type, const Transform3d trmat, const DataBase &data)
{
GUI_App &app = wxGetApp();
Plater *plater = app.plater();
ObjectList *obj_list = app.obj_list();
GLCanvas3D *canvas = plater->canvas3D();
ModelObjectPtrs &objects = plater->model().objects;
// Parent object for text volume was propably removed.
// Assumption: User know what he does, so text volume is no more needed.
if (objects.size() <= object_idx)
return priv::create_message(_u8L("Bad object index to create volume."));
if (mesh.its.empty())
return priv::create_message(_u8L("Can't create empty volume."));
plater->take_snapshot(_L("Add Emboss text Volume"));
ModelObject *obj = objects[object_idx];
// NOTE: be carefull add volume also center mesh !!!
// So first add simple shape(convex hull is also calculated)
ModelVolume *volume = obj->add_volume(make_cube(1., 1., 1.), type);
// TODO: Refactor to create better way to not set cube at begining
// Revert mesh centering by set mesh after add cube
volume->set_mesh(std::move(mesh));
volume->calculate_convex_hull();
// set a default extruder value, since user can't add it manually
volume->config.set_key_value("extruder", new ConfigOptionInt(0));
// do not allow model reload from disk
volume->source.is_from_builtin_objects = true;
volume->name = data.volume_name; // copy
volume->text_configuration = data.text_configuration; // copy
volume->set_transformation(trmat);
// update volume name in object list
// updata selection after new volume added
// change name of volume in right panel
// select only actual volume
// when new volume is created change selection to this volume
auto add_to_selection = [volume](const ModelVolume *vol) { return vol == volume; };
wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(object_idx, add_to_selection);
if (!sel.IsEmpty()) obj_list->select_item(sel.front());
// update printable state on canvas
if (type == ModelVolumeType::MODEL_PART) canvas->update_instance_printable_state_for_object(object_idx);
obj_list->selection_changed();
// Now is valid text volume selected open emboss gizmo
GLGizmosManager &manager = canvas->get_gizmos_manager();
if (manager.get_current_type() != GLGizmosManager::Emboss)
manager.open_gizmo(GLGizmosManager::Emboss);
// redraw scene
canvas->reload_scene(true);
}
ModelVolume *priv::get_volume(ModelObjectPtrs &objects,
@ -664,7 +644,7 @@ ModelVolume *priv::get_volume(ModelObjectPtrs &objects,
return nullptr;
};
Emboss::OrthoProject priv::create_projection_for_cut(
OrthoProject priv::create_projection_for_cut(
Transform3d tr,
double shape_scale,
const std::pair<float, float> &z_range)
@ -686,10 +666,10 @@ Emboss::OrthoProject priv::create_projection_for_cut(
// Projection is in direction from far plane
tr.translate(Vec3d(0., 0., min_z));
tr.scale(shape_scale);
return Emboss::OrthoProject(tr, project_direction);
return OrthoProject(tr, project_direction);
}
Emboss::OrthoProject3d priv::create_emboss_projection(
OrthoProject3d priv::create_emboss_projection(
bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut)
{
// Offset of clossed side to model
@ -699,14 +679,98 @@ Emboss::OrthoProject3d priv::create_emboss_projection(
back_move = -((is_outside) ? surface_offset : emboss);
its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move)));
Vec3d from_front_to_back(0., 0., back_move - front_move);
return Emboss::OrthoProject3d(from_front_to_back);
return OrthoProject3d(from_front_to_back);
}
// input can't be const - cache of font
TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2, std::function<bool()> was_canceled)
{
const TextConfiguration &tc = input1.text_configuration;
const char *text = tc.text.c_str();
const FontProp &fp = tc.style.prop;
ExPolygons shapes = text2shapes(input1.font_file, text, fp, was_canceled);
if (shapes.empty() || shapes.front().contour.empty())
throw JobException(_u8L("Font doesn't have any shape for given text.").c_str());
if (was_canceled()) return {};
// Define alignment of text - left, right, center, top bottom, ....
BoundingBox bb = get_extents(shapes);
Point projection_center = bb.center();
for (ExPolygon &shape : shapes) shape.translate(-projection_center);
bb.translate(-projection_center);
const FontFile &ff = *input1.font_file.font_file;
double shape_scale = get_shape_scale(fp, ff);
const SurfaceVolumeData::ModelSources &sources = input2.sources;
const SurfaceVolumeData::ModelSource *biggest = nullptr;
size_t biggest_count = 0;
// convert index from (s)ources to (i)ndexed (t)riangle (s)ets
std::vector<size_t> s_to_itss(sources.size(), std::numeric_limits<size_t>::max());
std::vector<indexed_triangle_set> itss;
itss.reserve(sources.size());
for (const SurfaceVolumeData::ModelSource &s : sources) {
Transform3d mesh_tr_inv = s.tr.inverse();
Transform3d cut_projection_tr = mesh_tr_inv * input2.text_tr;
std::pair<float, float> z_range{0., 1.};
OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
// copy only part of source model
indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection);
if (its.indices.empty()) continue;
if (biggest_count < its.vertices.size()) {
biggest_count = its.vertices.size();
biggest = &s;
}
s_to_itss[&s - &sources.front()] = itss.size();
itss.emplace_back(std::move(its));
}
if (itss.empty()) throw JobException(_u8L("There is no volume in projection direction.").c_str());
Transform3d tr_inv = biggest->tr.inverse();
size_t itss_index = s_to_itss[biggest - &sources.front()];
BoundingBoxf3 mesh_bb = bounding_box(itss[itss_index]);
for (const SurfaceVolumeData::ModelSource &s : sources) {
if (&s == biggest) continue;
size_t itss_index = s_to_itss[&s - &sources.front()];
if (itss_index == std::numeric_limits<size_t>::max()) continue;
Transform3d tr = s.tr * tr_inv;
indexed_triangle_set &its = itss[itss_index];
its_transform(its, tr);
BoundingBoxf3 bb = bounding_box(its);
mesh_bb.merge(bb);
}
// tr_inv = transformation of mesh inverted
Transform3d cut_projection_tr = tr_inv * input2.text_tr;
Transform3d emboss_tr = cut_projection_tr.inverse();
BoundingBoxf3 mesh_bb_tr = mesh_bb.transformed(emboss_tr);
std::pair<float, float> z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()};
OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
float projection_ratio = (-z_range.first + safe_extension) / (z_range.second - z_range.first + 2 * safe_extension);
// Use CGAL to cut surface from triangle mesh
SurfaceCut cut = cut_surface(shapes, itss, cut_projection, projection_ratio);
if (cut.empty()) throw JobException(_u8L("There is no valid surface for text projection.").c_str());
if (was_canceled()) return {};
// !! Projection needs to transform cut
OrthoProject3d projection = create_emboss_projection(input2.is_outside, fp.emboss, emboss_tr, cut);
indexed_triangle_set new_its = cut2model(cut, projection);
assert(!new_its.empty());
if (was_canceled()) return {};
return TriangleMesh(std::move(new_its));
}
bool priv::process(std::exception_ptr &eptr) {
if (!eptr) return false;
try {
std::rethrow_exception(eptr);
} catch (priv::EmbossJobException &e) {
} catch (priv::JobException &e) {
create_message(e.what());
eptr = nullptr;
}

View File

@ -15,15 +15,15 @@ class ModelVolume;
class TriangleMesh;
}
namespace Slic3r::GUI {
namespace Slic3r::GUI::Emboss {
/// <summary>
/// Base data holder for embossing
/// </summary>
struct EmbossDataBase
struct DataBase
{
// Keep pointer on Data of font (glyph shapes)
Emboss::FontFileWithCache font_file;
Slic3r::Emboss::FontFileWithCache font_file;
// font item is not used for create object
TextConfiguration text_configuration;
// new volume name created from text
@ -35,24 +35,16 @@ struct EmbossDataBase
/// Volume is created on the surface of existing volume in object.
/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
/// </summary>
struct EmbossDataCreateVolume : public EmbossDataBase
struct DataCreateVolume : public DataBase
{
// define embossed volume type
ModelVolumeType volume_type;
// define position on screen where to create object
Vec2d screen_coor;
// parent ModelObject index where to create volume
int object_idx;
// projection property
Camera camera;
// used to find point on surface where to create new object
RaycastManager::SurfacePoint hit;
Transform3d hit_object_tr;
Transform3d hit_instance_tr;
// new created volume transformation
Transform3d trmat;
};
/// <summary>
@ -60,14 +52,13 @@ struct EmbossDataCreateVolume : public EmbossDataBase
/// Should not be stopped
/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
/// </summary>
class EmbossCreateVolumeJob : public Job
class CreateVolumeJob : public Job
{
EmbossDataCreateVolume m_input;
DataCreateVolume m_input;
TriangleMesh m_result;
Transform3d m_transformation;
public:
EmbossCreateVolumeJob(EmbossDataCreateVolume&& input);
CreateVolumeJob(DataCreateVolume&& input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &eptr) override;
};
@ -77,7 +68,7 @@ public:
/// Object is placed on bed under screen coor
/// OR to center of scene when it is out of bed shape
/// </summary>
struct EmbossDataCreateObject : public EmbossDataBase
struct DataCreateObject : public DataBase
{
// define position on screen where to create object
Vec2d screen_coor;
@ -93,13 +84,13 @@ struct EmbossDataCreateObject : public EmbossDataBase
/// Create new TextObject on the platter
/// Should not be stopped
/// </summary>
class EmbossCreateObjectJob : public Job
class CreateObjectJob : public Job
{
EmbossDataCreateObject m_input;
DataCreateObject m_input;
TriangleMesh m_result;
Transform3d m_transformation;
public:
EmbossCreateObjectJob(EmbossDataCreateObject&& input);
CreateObjectJob(DataCreateObject&& input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &eptr) override;
};
@ -107,7 +98,7 @@ public:
/// <summary>
/// Hold neccessary data to update embossed text object in job
/// </summary>
struct EmbossDataUpdate : public EmbossDataBase
struct DataUpdate : public DataBase
{
// unique identifier of volume to change
ObjectID volume_id;
@ -121,14 +112,14 @@ struct EmbossDataUpdate : public EmbossDataBase
/// Update text shape in existing text volume
/// Predict that there is only one runnig(not canceled) instance of it
/// </summary>
class EmbossUpdateJob : public Job
class UpdateJob : public Job
{
EmbossDataUpdate m_input;
DataUpdate m_input;
TriangleMesh m_result;
public:
// move params to private variable
EmbossUpdateJob(EmbossDataUpdate &&input);
UpdateJob(DataUpdate &&input);
/// <summary>
/// Create new embossed volume by m_input data and store to m_result
@ -158,10 +149,7 @@ public:
const std::string &volume_name);
};
/// <summary>
/// Hold neccessary data to update embossed text object in job
/// </summary>
struct UseSurfaceData : public EmbossDataUpdate
struct SurfaceVolumeData
{
// Transformation of text volume inside of object
Transform3d text_tr;
@ -180,26 +168,66 @@ struct UseSurfaceData : public EmbossDataUpdate
};
using ModelSources = std::vector<ModelSource>;
ModelSources sources;
};
/// <summary>
/// Hold neccessary data to create(cut) volume from surface object in job
/// </summary>
struct CreateSurfaceVolumeData : public DataBase, public SurfaceVolumeData{
// define embossed volume type
ModelVolumeType volume_type;
// parent ModelObject index where to create volume
int object_idx;
};
/// <summary>
/// Cut surface from object and create cutted volume
/// Should not be stopped
/// </summary>
class CreateSurfaceVolumeJob : public Job
{
CreateSurfaceVolumeData m_input;
TriangleMesh m_result;
public:
CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &eptr) override;
};
/// <summary>
/// Hold neccessary data to update embossed text object in job
/// </summary>
struct UpdateSurfaceVolumeData : public DataUpdate, public SurfaceVolumeData{};
/// <summary>
/// Copied triangles from object to be able create mesh for cut surface from
/// </summary>
/// <param name="volumes">Source object volumes for cut surface from</param>
/// <param name="text_volume_id">Source volume id</param>
/// <returns>Source data for cut surface from</returns>
SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> text_volume_id = {});
/// <summary>
/// Copied triangles from object to be able create mesh for cut surface from
/// </summary>
/// <param name="text_volume">Define text in object</param>
/// <returns>Source data for cut surface from</returns>
static ModelSources create_sources(const ModelVolume *text_volume);
};
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume);
/// <summary>
/// Update text volume to use surface from object
/// </summary>
class UseSurfaceJob : public Job
class UpdateSurfaceVolumeJob : public Job
{
UseSurfaceData m_input;
UpdateSurfaceVolumeData m_input;
TriangleMesh m_result;
public:
// move params to private variable
UseSurfaceJob(UseSurfaceData &&input);
UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &eptr) override;
};

View File

@ -12,9 +12,10 @@
#include "slic3r/Utils/EmbossStylesSerializable.hpp"
using namespace Slic3r;
using namespace Slic3r::GUI;
using namespace Slic3r::Emboss;
using namespace Slic3r::GUI::Emboss;
EmbossStyleManager::EmbossStyleManager(const ImWchar *language_glyph_range)
StyleManager::StyleManager(const ImWchar *language_glyph_range)
: m_imgui_init_glyph_range(language_glyph_range)
, m_exist_style_images(false)
, m_temp_style_images(nullptr)
@ -22,12 +23,12 @@ EmbossStyleManager::EmbossStyleManager(const ImWchar *language_glyph_range)
, m_last_style_index(std::numeric_limits<size_t>::max())
{}
EmbossStyleManager::~EmbossStyleManager() {
StyleManager::~StyleManager() {
clear_imgui_font();
free_style_images();
}
void EmbossStyleManager::init(AppConfig *app_config, const EmbossStyles &default_styles)
void StyleManager::init(AppConfig *app_config, const EmbossStyles &default_styles)
{
m_app_config = app_config;
EmbossStyles styles = (app_config != nullptr) ?
@ -67,7 +68,7 @@ void EmbossStyleManager::init(AppConfig *app_config, const EmbossStyles &default
}
}
bool EmbossStyleManager::store_styles_to_app_config(bool use_modification,
bool StyleManager::store_styles_to_app_config(bool use_modification,
bool store_activ_index)
{
assert(m_app_config != nullptr);
@ -102,7 +103,7 @@ bool EmbossStyleManager::store_styles_to_app_config(bool use_modification,
return true;
}
void EmbossStyleManager::add_style(const std::string &name) {
void StyleManager::add_style(const std::string &name) {
EmbossStyle& style = m_style_cache.style;
style.name = name;
make_unique_name(style.name);
@ -112,7 +113,7 @@ void EmbossStyleManager::add_style(const std::string &name) {
m_style_items.push_back({style});
}
void EmbossStyleManager::swap(size_t i1, size_t i2) {
void StyleManager::swap(size_t i1, size_t i2) {
if (i1 >= m_style_items.size() ||
i2 >= m_style_items.size()) return;
std::swap(m_style_items[i1], m_style_items[i2]);
@ -125,7 +126,7 @@ void EmbossStyleManager::swap(size_t i1, size_t i2) {
}
}
void EmbossStyleManager::discard_style_changes() {
void StyleManager::discard_style_changes() {
if (exist_stored_style()) {
if (load_style(m_style_cache.style_index))
return; // correct reload style
@ -138,7 +139,7 @@ void EmbossStyleManager::discard_style_changes() {
load_first_valid_font();
}
void EmbossStyleManager::erase(size_t index) {
void StyleManager::erase(size_t index) {
if (index >= m_style_items.size()) return;
// fix selected index
@ -151,7 +152,7 @@ void EmbossStyleManager::erase(size_t index) {
m_style_items.erase(m_style_items.begin() + index);
}
void EmbossStyleManager::rename(const std::string& name) {
void StyleManager::rename(const std::string& name) {
m_style_cache.style.name = name;
m_style_cache.truncated_name.clear();
if (exist_stored_style()) {
@ -161,7 +162,7 @@ void EmbossStyleManager::rename(const std::string& name) {
}
}
bool EmbossStyleManager::load_style(size_t style_index)
bool StyleManager::load_style(size_t style_index)
{
if (style_index >= m_style_items.size()) return false;
if (!load_style(m_style_items[style_index].style)) return false;
@ -171,14 +172,14 @@ bool EmbossStyleManager::load_style(size_t style_index)
return true;
}
bool EmbossStyleManager::load_style(const EmbossStyle &style) {
bool StyleManager::load_style(const EmbossStyle &style) {
if (style.type == EmbossStyle::Type::file_path) {
std::unique_ptr<Emboss::FontFile> font_ptr =
Emboss::create_font_file(style.path.c_str());
std::unique_ptr<FontFile> font_ptr =
create_font_file(style.path.c_str());
if (font_ptr == nullptr) return false;
m_style_cache.wx_font = {};
m_style_cache.font_file =
Emboss::FontFileWithCache(std::move(font_ptr));
FontFileWithCache(std::move(font_ptr));
m_style_cache.style = style; // copy
m_style_cache.style_index = std::numeric_limits<size_t>::max();
m_style_cache.stored_wx_font = {};
@ -190,7 +191,7 @@ bool EmbossStyleManager::load_style(const EmbossStyle &style) {
return load_style(style, *wx_font_opt);
}
bool EmbossStyleManager::load_style(const EmbossStyle &style, const wxFont &font)
bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font)
{
if (!set_wx_font(font)) return false;
m_style_cache.style = style; // copy
@ -200,9 +201,9 @@ bool EmbossStyleManager::load_style(const EmbossStyle &style, const wxFont &font
return true;
}
bool EmbossStyleManager::is_activ_font() { return m_style_cache.font_file.has_value(); }
bool StyleManager::is_activ_font() { return m_style_cache.font_file.has_value(); }
bool EmbossStyleManager::load_first_valid_font() {
bool StyleManager::load_first_valid_font() {
while (!m_style_items.empty()) {
if (load_style(0)) return true;
// can't load so erase it from list
@ -211,22 +212,22 @@ bool EmbossStyleManager::load_first_valid_font() {
return false;
}
const EmbossStyle* EmbossStyleManager::get_stored_style() const
const EmbossStyle* StyleManager::get_stored_style() const
{
if (m_style_cache.style_index >= m_style_items.size()) return nullptr;
return &m_style_items[m_style_cache.style_index].style;
}
void EmbossStyleManager::clear_glyphs_cache()
void StyleManager::clear_glyphs_cache()
{
Emboss::FontFileWithCache &ff = m_style_cache.font_file;
FontFileWithCache &ff = m_style_cache.font_file;
if (!ff.has_value()) return;
ff.cache = std::make_shared<Emboss::Glyphs>();
ff.cache = std::make_shared<Glyphs>();
}
void EmbossStyleManager::clear_imgui_font() { m_style_cache.atlas.Clear(); }
void StyleManager::clear_imgui_font() { m_style_cache.atlas.Clear(); }
ImFont *EmbossStyleManager::get_imgui_font()
ImFont *StyleManager::get_imgui_font()
{
if (!is_activ_font()) return nullptr;
@ -242,16 +243,16 @@ ImFont *EmbossStyleManager::get_imgui_font()
return font;
}
const std::vector<EmbossStyleManager::Item> &EmbossStyleManager::get_styles() const{ return m_style_items; }
const std::vector<StyleManager::Item> &StyleManager::get_styles() const{ return m_style_items; }
ImFont* EmbossStyleManager::extend_imgui_font_range(size_t index, const std::string& text)
ImFont* StyleManager::extend_imgui_font_range(size_t index, const std::string& text)
{
// TODO: start using merge mode
// ImFontConfig::MergeMode = true;
return create_imgui_font(text);
}
void EmbossStyleManager::make_unique_name(std::string &name)
void StyleManager::make_unique_name(std::string &name)
{
auto is_unique = [&](const std::string &name) -> bool {
for (const Item &it : m_style_items)
@ -283,7 +284,7 @@ void EmbossStyleManager::make_unique_name(std::string &name)
name = new_name;
}
void EmbossStyleManager::init_trunc_names(float max_width) {
void StyleManager::init_trunc_names(float max_width) {
for (auto &s : m_style_items)
if (s.truncated_name.empty()) {
std::string name = s.style.name;
@ -298,7 +299,7 @@ void EmbossStyleManager::init_trunc_names(float max_width) {
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
void EmbossStyleManager::init_style_images(const Vec2i &max_size,
void StyleManager::init_style_images(const Vec2i &max_size,
const std::string &text)
{
// check already initialized
@ -311,7 +312,7 @@ void EmbossStyleManager::init_style_images(const Vec2i &max_size,
assert(m_temp_style_images->images.size() ==
m_temp_style_images->styles.size());
// copy images into styles
for (EmbossStyleManager::StyleImage &image : m_temp_style_images->images){
for (StyleManager::StyleImage &image : m_temp_style_images->images){
size_t index = &image - &m_temp_style_images->images.front();
StyleImagesData::Item &style = m_temp_style_images->styles[index];
@ -340,11 +341,11 @@ void EmbossStyleManager::init_style_images(const Vec2i &max_size,
const EmbossStyle &style = item.style;
std::optional<wxFont> wx_font_opt = WxFontUtils::load_wxFont(style.path);
if (!wx_font_opt.has_value()) continue;
std::unique_ptr<Emboss::FontFile> font_file =
std::unique_ptr<FontFile> font_file =
WxFontUtils::create_font_file(*wx_font_opt);
if (font_file == nullptr) continue;
styles.push_back({
Emboss::FontFileWithCache(std::move(font_file)),
FontFileWithCache(std::move(font_file)),
style.name,
style.prop
});
@ -354,7 +355,7 @@ void EmbossStyleManager::init_style_images(const Vec2i &max_size,
queue_job(worker, std::make_unique<CreateFontStyleImagesJob>(std::move(data)));
}
void EmbossStyleManager::free_style_images() {
void StyleManager::free_style_images() {
if (!m_exist_style_images) return;
GLuint tex_id = 0;
for (Item &it : m_style_items) {
@ -367,10 +368,10 @@ void EmbossStyleManager::free_style_images() {
m_exist_style_images = false;
}
float EmbossStyleManager::min_imgui_font_size = 18.f;
float EmbossStyleManager::max_imgui_font_size = 60.f;
float EmbossStyleManager::get_imgui_font_size(const FontProp &prop,
const Emboss::FontFile &file)
float StyleManager::min_imgui_font_size = 18.f;
float StyleManager::max_imgui_font_size = 60.f;
float StyleManager::get_imgui_font_size(const FontProp &prop,
const FontFile &file)
{
const auto &cn = prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
@ -384,12 +385,12 @@ float EmbossStyleManager::get_imgui_font_size(const FontProp &prop,
return c1 * std::abs(prop.size_in_mm) / 0.3528f;
}
ImFont *EmbossStyleManager::create_imgui_font(const std::string &text)
ImFont *StyleManager::create_imgui_font(const std::string &text)
{
// inspiration inside of ImGuiWrapper::init_font
auto& ff = m_style_cache.font_file;
if (!ff.has_value()) return nullptr;
const Emboss::FontFile &font_file = *ff.font_file;
const FontFile &font_file = *ff.font_file;
ImFontGlyphRangesBuilder builder;
builder.AddRanges(m_imgui_init_glyph_range);
@ -464,18 +465,18 @@ ImFont *EmbossStyleManager::create_imgui_font(const std::string &text)
return font;
}
bool EmbossStyleManager::set_wx_font(const wxFont &wx_font) {
std::unique_ptr<Emboss::FontFile> font_file =
bool StyleManager::set_wx_font(const wxFont &wx_font) {
std::unique_ptr<FontFile> font_file =
WxFontUtils::create_font_file(wx_font);
return set_wx_font(wx_font, std::move(font_file));
}
bool EmbossStyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr<Emboss::FontFile> font_file)
bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr<FontFile> font_file)
{
if (font_file == nullptr) return false;
m_style_cache.wx_font = wx_font; // copy
m_style_cache.font_file =
Emboss::FontFileWithCache(std::move(font_file));
FontFileWithCache(std::move(font_file));
EmbossStyle &style = m_style_cache.style;
style.type = WxFontUtils::get_actual_type();

View File

@ -11,24 +11,24 @@
class wxFont;
namespace Slic3r::GUI {
namespace Slic3r::GUI::Emboss {
/// <summary>
/// Manage Emboss text styles
/// Cache actual state of style
/// + imgui font
/// + wx font
/// </summary>
class EmbossStyleManager
class StyleManager
{
friend class CreateFontStyleImagesJob; // access to StyleImagesData
public:
EmbossStyleManager(const ImWchar *language_glyph_range);
StyleManager(const ImWchar *language_glyph_range);
/// <summary>
/// Release imgui font and style images from GPU
/// </summary>
~EmbossStyleManager();
~StyleManager();
/// <summary>
/// Load font style list from config
@ -111,7 +111,7 @@ public:
FontProp &get_font_prop() { return get_style().prop; }
const std::optional<wxFont> &get_wx_font() const { return m_style_cache.wx_font; }
const std::optional<wxFont> &get_stored_wx_font() const { return m_style_cache.stored_wx_font; }
Emboss::FontFileWithCache &get_font_file_with_cache() { return m_style_cache.font_file; }
Slic3r::Emboss::FontFileWithCache &get_font_file_with_cache() { return m_style_cache.font_file; }
bool has_collections() const { return m_style_cache.font_file.font_file != nullptr &&
m_style_cache.font_file.font_file->infos.size() > 1; }
@ -132,7 +132,7 @@ public:
/// <param name="wx_font">Must be source of font file</param>
/// <param name="font_file">font file created by WxFontUtils::create_font_file(wx_font)</param>
/// <returns>True on success otherwise false</returns>
bool set_wx_font(const wxFont &wx_font, std::unique_ptr<Emboss::FontFile> font_file );
bool set_wx_font(const wxFont &wx_font, std::unique_ptr<Slic3r::Emboss::FontFile> font_file);
// Getter on acitve font pointer for imgui
// Initialize imgui font(generate texture) when doesn't exist yet.
@ -191,7 +191,7 @@ public:
// Value out of limits is crop
static float min_imgui_font_size;
static float max_imgui_font_size;
static float get_imgui_font_size(const FontProp& prop, const Emboss::FontFile& file);
static float get_imgui_font_size(const FontProp &prop, const Slic3r::Emboss::FontFile &file);
private:
// erase font when not possible to load
@ -207,7 +207,7 @@ private:
struct StyleCache
{
// share font file data with emboss job thread
Emboss::FontFileWithCache font_file = {};
Slic3r::Emboss::FontFileWithCache font_file = {};
// must live same as imgui_font inside of atlas
ImVector<ImWchar> ranges = {};
@ -251,7 +251,7 @@ private:
{
struct Item
{
Emboss::FontFileWithCache font;
Slic3r::Emboss::FontFileWithCache font;
std::string text;
FontProp prop;
};

View File

@ -51,7 +51,7 @@ public:
/// <param name="font">wx descriptor of font</param>
/// <param name="font_file">file described in wx font</param>
/// <returns>New created font fileon success otherwise nullptr</returns>
static std::unique_ptr<Emboss::FontFile> set_italic(wxFont &font, const Emboss::FontFile& prev_font_file);
static std::unique_ptr<Slic3r::Emboss::FontFile> set_italic(wxFont &font, const Slic3r::Emboss::FontFile &prev_font_file);
/// <summary>
/// Set boldness into wx font
@ -61,7 +61,7 @@ public:
/// <param name="font">wx descriptor of font</param>
/// <param name="font_file">file described in wx font</param>
/// <returns>New created font fileon success otherwise nullptr</returns>
static std::unique_ptr<Emboss::FontFile> set_bold(wxFont &font, const Emboss::FontFile& font_file);
static std::unique_ptr<Slic3r::Emboss::FontFile> set_bold(wxFont &font, const Slic3r::Emboss::FontFile &font_file);
// convert wxFont types to string and vice versa
static const boost::bimap<wxFontFamily, std::string_view> type_to_family;