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" #include "libslic3r/Line.hpp"
using namespace Slic3r; using namespace Slic3r;
double Emboss::SHAPE_SCALE = 0.001;//SCALING_FACTOR;
// do not expose out of this file stbtt_ data types // do not expose out of this file stbtt_ data types
namespace priv{ namespace priv{

View File

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

View File

@ -936,23 +936,8 @@ private:
// 1 -> is splittable // 1 -> is splittable
mutable int m_is_splittable{ -1 }; 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->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());
if (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->config.id().valid());
assert(this->supported_facets.id().valid()); assert(this->supported_facets.id().valid());
assert(this->seam_facets.id().valid()); assert(this->seam_facets.id().valid());
@ -961,6 +946,24 @@ private:
assert(this->id() != this->supported_facets.id()); assert(this->id() != this->supported_facets.id());
assert(this->id() != this->seam_facets.id()); assert(this->id() != this->seam_facets.id());
assert(this->id() != this->mmu_segmentation_facets.id()); assert(this->id() != this->mmu_segmentation_facets.id());
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(check());
} }
// Copying an existing volume, therefore this volume will get a copy of the ID assigned. // 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 #endif // ALLOW_DEBUG_MODE
using namespace Slic3r; using namespace Slic3r;
using namespace Slic3r::Emboss;
using namespace Slic3r::GUI; using namespace Slic3r::GUI;
using namespace Slic3r::GUI::Emboss;
// anonymous namespace for unique names // anonymous namespace for unique names
namespace { namespace {
@ -165,6 +167,18 @@ void GLGizmoEmboss::set_fine_position()
ImGuiWrapper::draw(rect); 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) void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
{ {
assert(volume_type == ModelVolumeType::MODEL_PART || assert(volume_type == ModelVolumeType::MODEL_PART ||
@ -188,11 +202,17 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
Plater* plater = wxGetApp().plater(); Plater* plater = wxGetApp().plater();
const Camera &camera = plater->get_camera(); const Camera &camera = plater->get_camera();
const Pointfs &bed_shape = plater->build_volume().bed_shape(); const Pointfs &bed_shape = plater->build_volume().bed_shape();
EmbossDataCreateObject data{create_emboss_data_base(), // TODO: Fix double creation of base data (first is inside function start_volume_creation)
screen_coor, DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager);
camera, FontProp& prop = emboss_data.text_configuration.style.prop;
bed_shape};
auto job = std::make_unique<EmbossCreateObjectJob>(std::move(data)); // 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(); Worker &worker = plater->get_ui_job_worker();
queue_job(worker, std::move(job)); queue_job(worker, std::move(job));
} }
@ -369,9 +389,9 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
// Calculate temporary position // Calculate temporary position
Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); 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; 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 // fix baked transformation from .3mf store process
if (tc.fix_3mf_tr.has_value()) 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"); } void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); }
bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type, bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type, const Vec2d &screen_coor)
const Vec2d &screen_coor)
{ {
Plater* plater = wxGetApp().plater(); Plater* plater = wxGetApp().plater();
@ -848,18 +867,33 @@ bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type,
GLVolume *gl_volume = volumes[hovered_id]; GLVolume *gl_volume = volumes[hovered_id];
Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix(); Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix();
// create volume DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager);
EmbossDataCreateVolume data{create_emboss_data_base(),
volume_type,
screen_coor,
object_idx_signed,
camera,
*hit,
hit_object_trmat,
hit_instance_trmat};
// 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(); Worker &worker = plater->get_ui_job_worker();
auto job = std::make_unique<EmbossCreateVolumeJob>(std::move(data));
queue_job(worker, std::move(job)); queue_job(worker, std::move(job));
return true; return true;
} }
@ -957,7 +991,7 @@ bool GLGizmoEmboss::process()
m_update_job_cancel->store(true); m_update_job_cancel->store(true);
// create new shared ptr to cancel new job // create new shared ptr to cancel new job
m_update_job_cancel = std::make_shared<std::atomic<bool> >(false); 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; std::unique_ptr<Job> job = nullptr;
@ -965,7 +999,7 @@ bool GLGizmoEmboss::process()
const TextConfiguration &tc = data.text_configuration; const TextConfiguration &tc = data.text_configuration;
if (tc.style.prop.use_surface) { if (tc.style.prop.use_surface) {
// Model to cut surface from. // 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; if (sources.empty()) return false;
Transform3d text_tr = m_volume->get_matrix(); Transform3d text_tr = m_volume->get_matrix();
@ -977,11 +1011,10 @@ bool GLGizmoEmboss::process()
// check that there is not unexpected volume type // check that there is not unexpected volume type
assert(is_outside || m_volume->is_negative_volume() || assert(is_outside || m_volume->is_negative_volume() ||
m_volume->is_modifier()); m_volume->is_modifier());
UseSurfaceData surface_data{std::move(data), text_tr, is_outside, UpdateSurfaceVolumeData surface_data{std::move(data), text_tr, is_outside, std::move(sources)};
std::move(sources)}; job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
job = std::make_unique<UseSurfaceJob>(std::move(surface_data));
} else { } 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() { void GLGizmoEmboss::discard_and_close() {
if (!m_unmodified_volume.has_value()) return; if (!m_unmodified_volume.has_value()) return;
m_volume->set_transformation(m_unmodified_volume->tr); 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(); close();
//auto plater = wxGetApp().plater(); //auto plater = wxGetApp().plater();
@ -1139,18 +1172,18 @@ void GLGizmoEmboss::draw_window()
void GLGizmoEmboss::draw_text_input() 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(); auto& ff = mng.get_font_file_with_cache();
assert(ff.has_value()); assert(ff.has_value());
const auto &cn = mng.get_font_prop().collection_number; const auto &cn = mng.get_font_prop().collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0; 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(); ImFont *imgui_font = m_style_manager.get_imgui_font();
if (imgui_font == nullptr) { if (imgui_font == nullptr) {
// try create new imgui font // 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(); imgui_font = m_style_manager.get_imgui_font();
} }
bool exist_font = bool exist_font =
@ -1190,10 +1223,10 @@ void GLGizmoEmboss::draw_text_input()
if (prop.line_gap.has_value()) if (prop.line_gap.has_value())
append_warning(_u8L("Line gap"), _u8L("Unsupported visualization of gap between lines inside text input.")); 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(); auto &ff = m_style_manager.get_font_file_with_cache();
float imgui_size = EmbossStyleManager::get_imgui_font_size(prop, *ff.font_file); float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file);
if (imgui_size > EmbossStyleManager::max_imgui_font_size) if (imgui_size > StyleManager::max_imgui_font_size)
append_warning(_u8L("To tall"), _u8L("Diminished font height inside text input.")); 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.")); append_warning(_u8L("To small"), _u8L("Enlarged font height inside text input."));
if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who); 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; const ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll;
if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) { if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) {
process(); process();
range_text = create_range_text(); range_text = create_range_text_prep();
} }
if (exist_font) ImGui::PopFont(); if (exist_font) ImGui::PopFont();
@ -1571,7 +1604,7 @@ void GLGizmoEmboss::draw_font_list()
// When is unknown font is inside .3mf only font selection is allowed // When is unknown font is inside .3mf only font selection is allowed
// Stop Imgui disable + Guard again start disabling // Stop Imgui disable + Guard again start disabling
ScopeGuard unknown_font_sc; ScopeGuard unknown_font_sc;
if (m_is_unknown_font) { if (m_is_unknown_font) {
m_imgui->disabled_end(); m_imgui->disabled_end();
unknown_font_sc = ScopeGuard([&]() { unknown_font_sc = ScopeGuard([&]() {
m_imgui->disabled_begin(true); m_imgui->disabled_begin(true);
@ -1963,7 +1996,7 @@ void GLGizmoEmboss::draw_delete_style_button() {
void GLGizmoEmboss::draw_revert_all_styles_button() { void GLGizmoEmboss::draw_revert_all_styles_button() {
if (draw_button(IconType::revert_all)) { 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()); m_style_manager.init(nullptr, create_default_styles());
assert(m_style_manager.get_font_file_with_cache().has_value()); assert(m_style_manager.get_font_file_with_cache().has_value());
process(); 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_style_images(m_gui_cfg->max_style_image_size, m_text);
m_style_manager.init_trunc_names(max_style_name_width); m_style_manager.init_trunc_names(max_style_name_width);
std::optional<std::pair<size_t,size_t>> swap_indexes; 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) { for (const auto &item : styles) {
size_t index = &item - &styles.front(); size_t index = &item - &styles.front();
const EmbossStyle &style = item.style; const EmbossStyle &style = item.style;
@ -2038,7 +2071,7 @@ void GLGizmoEmboss::draw_style_list() {
bool is_selected = (index == m_style_manager.get_style_index()); 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 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 // allow click delete button
ImGuiSelectableFlags_ flags = ImGuiSelectableFlags_AllowItemOverlap; ImGuiSelectableFlags_ flags = ImGuiSelectableFlags_AllowItemOverlap;
if (ImGui::Selectable(item.truncated_name.c_str(), is_selected, flags, select_size)) { 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); for (const auto &p : polys) bb.merge(p.contour.points);
const FontProp &fp = m_style_manager.get_style().prop; const FontProp &fp = m_style_manager.get_style().prop;
float scale = fp.size_in_mm / std::max(bb.max.x(), bb.max.y()); float scale = fp.size_in_mm / std::max(bb.max.x(), bb.max.y());
auto project = std::make_unique<Emboss::ProjectScale>( auto project = std::make_unique<ProjectScale>(
std::make_unique<Emboss::ProjectZ>(fp.emboss / scale), scale); std::make_unique<ProjectZ>(fp.emboss / scale), scale);
indexed_triangle_set its = Emboss::polygons2model(polys, *project); indexed_triangle_set its = polygons2model(polys, *project);
return false; return false;
// test store: // test store:
// for (auto &poly : polys) poly.scale(1e5); // for (auto &poly : polys) poly.scale(1e5);
@ -2929,41 +2962,38 @@ bool GLGizmoEmboss::choose_svg_file()
//return add_volume(name, its); //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 = [&]() { 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; std::string text_fixed;
if (contain_enter) { if (contain_enter) {
// change enters to space // change enters to space
text_fixed = m_text; // copy text_fixed = text; // copy
std::replace(text_fixed.begin(), text_fixed.end(), '\n', ' '); 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 { 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"); 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}; TextConfiguration tc{es, default_text_for_emboss};
// TODO: investigate how to initialize // TODO: investigate how to initialize
return tc; 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 // actualize font path - during changes in gui it could be corrupted
// volume must store valid path // volume must store valid path
assert(m_style_manager.get_wx_font().has_value()); assert(style_manager.get_wx_font().has_value());
assert(es.path.compare(WxFontUtils::store_wxFont(*m_style_manager.get_wx_font())) == 0); assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0);
//style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font()); // 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) bool GLGizmoEmboss::load_configuration(ModelVolume *volume)
@ -2974,7 +3004,7 @@ bool GLGizmoEmboss::load_configuration(ModelVolume *volume)
const TextConfiguration &tc = *tc_opt; const TextConfiguration &tc = *tc_opt;
const EmbossStyle &style = tc.style; 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; const EmbossStyle &es = style_item.style;
return es.name == style.name; return es.name == style.name;
}; };

View File

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

View File

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

View File

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

View File

@ -19,7 +19,9 @@
#include "slic3r/Utils/UndoRedo.hpp" #include "slic3r/Utils/UndoRedo.hpp"
using namespace Slic3r; using namespace Slic3r;
using namespace GUI; using namespace Slic3r::Emboss;
using namespace Slic3r::GUI;
using namespace Slic3r::GUI::Emboss;
// private namespace // private namespace
namespace priv{ namespace priv{
@ -31,11 +33,12 @@ constexpr float safe_extension = 1.0f;
/// </summary> /// </summary>
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
bool check(const EmbossDataBase &input, bool check_fontfile = true); bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
bool check(const EmbossDataCreateVolume &input, bool is_main_thread = false); bool check(const DataCreateVolume &input, bool is_main_thread = false);
bool check(const EmbossDataCreateObject &input); bool check(const DataCreateObject &input);
bool check(const EmbossDataUpdate &input, bool is_main_thread = false); bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
bool check(const UseSurfaceData &input, bool is_main_thread = false); bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
// <summary> // <summary>
/// Try to create mesh from text /// 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> /// NOTE: Cache glyphs is changed</param>
/// <param name="was_canceled">To check if process was canceled</param> /// <param name="was_canceled">To check if process was canceled</param>
/// <returns>Triangle mesh model</returns> /// <returns>Triangle mesh model</returns>
template<typename Fnc> template<typename Fnc> static TriangleMesh try_create_mesh(const DataBase &input, FontFileWithCache &font, Fnc was_canceled);
static TriangleMesh try_create_mesh(const EmbossDataBase &input, template<typename Fnc> static TriangleMesh create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl &ctl);
Emboss::FontFileWithCache &font,
Fnc was_canceled);
template<typename Fnc>
static TriangleMesh create_mesh(EmbossDataBase &input,
Fnc was_canceled,
Job::Ctl &ctl);
/// <summary> /// <summary>
/// Create default mesh for embossed text /// Create default mesh for embossed text
@ -66,7 +63,18 @@ static TriangleMesh create_default_mesh();
/// </summary> /// </summary>
/// <param name="mesh">New mesh data</param> /// <param name="mesh">New mesh data</param>
/// <param name="data">Text configuration, ...</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> /// <summary>
/// Select Volume from objects /// 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="objects">All objects in scene</param>
/// <param name="volume_id">Identifier of volume in object</param> /// <param name="volume_id">Identifier of volume in object</param>
/// <returns>Pointer to volume when exist otherwise nullptr</returns> /// <returns>Pointer to volume when exist otherwise nullptr</returns>
static ModelVolume *get_volume(ModelObjectPtrs &objects, static ModelVolume *get_volume(ModelObjectPtrs &objects, const ObjectID &volume_id);
const ObjectID &volume_id);
/// <summary> /// <summary>
/// Create projection for cut surface from mesh /// 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="shape_scale">Convert shape to milimeters</param>
/// <param name="z_range">Bounding box 3d of model volume for projection ranges</param> /// <param name="z_range">Bounding box 3d of model volume for projection ranges</param>
/// <returns>Orthogonal cut_projection</returns> /// <returns>Orthogonal cut_projection</returns>
static Emboss::OrthoProject create_projection_for_cut( static OrthoProject create_projection_for_cut(Transform3d tr, double shape_scale, const std::pair<float, float> &z_range);
Transform3d tr,
double shape_scale,
const std::pair<float, float> &z_range);
/// <summary> /// <summary>
/// Create tranformation for emboss Cutted surface /// 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="tr">Text voliume transformation inside object</param>
/// <param name="cut">Cutted surface from model</param> /// <param name="cut">Cutted surface from model</param>
/// <returns>Projection</returns> /// <returns>Projection</returns>
static Emboss::OrthoProject3d create_emboss_projection( static OrthoProject3d create_emboss_projection(bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut);
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 void create_message(const std::string &message); // only in finalize
static bool process(std::exception_ptr &eptr); static bool process(std::exception_ptr &eptr);
class EmbossJobException : public std::runtime_error class JobException : public std::runtime_error {
{ public: EmbossJobException(const char* message):runtime_error(message){}}; public: JobException(const char* message):runtime_error(message){}};
}// namespace priv }// namespace priv
///////////////// /////////////////
/// Create Volume /// Create Volume
EmbossCreateVolumeJob::EmbossCreateVolumeJob(EmbossDataCreateVolume &&input) CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input)
: m_input(std::move(input)) : m_input(std::move(input))
{ {
assert(priv::check(m_input, true)); 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."); if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossCreateVolumeJob.");
auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); }; auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); };
m_result = priv::create_mesh(m_input, was_canceled, ctl); m_result = priv::create_mesh(m_input, was_canceled, ctl);
if (was_canceled()) return; // center result
Vec3f c = m_result.bounding_box().center().cast<float>();
// Create new volume inside of object if (!c.isApprox(Vec3f::Zero())) m_result.translate(-c);
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;
} }
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 // doesn't care about exception when process was canceled by user
if (canceled) { if (canceled) {
eptr = nullptr; eptr = nullptr;
@ -141,74 +147,19 @@ void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
if (m_result.its.empty()) if (m_result.its.empty())
return priv::create_message(_u8L("Can't create empty volume.")); return priv::create_message(_u8L("Can't create empty volume."));
GUI_App &app = wxGetApp(); priv::create_volume(std::move(m_result), m_input.object_idx, m_input.volume_type, m_input.trmat, m_input);
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);
} }
///////////////// /////////////////
/// Create Object /// Create Object
EmbossCreateObjectJob::EmbossCreateObjectJob(EmbossDataCreateObject &&input) CreateObjectJob::CreateObjectJob(DataCreateObject &&input)
: m_input(std::move(input)) : m_input(std::move(input))
{ {
assert(priv::check(m_input)); assert(priv::check(m_input));
} }
void EmbossCreateObjectJob::process(Ctl &ctl) void CreateObjectJob::process(Ctl &ctl)
{ {
if (!priv::check(m_input)) if (!priv::check(m_input))
throw std::runtime_error("Bad input data for EmbossCreateObjectJob."); throw std::runtime_error("Bad input data for EmbossCreateObjectJob.");
@ -240,7 +191,7 @@ void EmbossCreateObjectJob::process(Ctl &ctl)
m_transformation = Transform3d(tt); 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 // doesn't care about exception when process was canceled by user
if (canceled) { if (canceled) {
@ -279,13 +230,13 @@ void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr)
///////////////// /////////////////
/// Update Volume /// Update Volume
EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input) UpdateJob::UpdateJob(DataUpdate&& input)
: m_input(std::move(input)) : m_input(std::move(input))
{ {
assert(priv::check(m_input, true)); assert(priv::check(m_input, true));
} }
void EmbossUpdateJob::process(Ctl &ctl) void UpdateJob::process(Ctl &ctl)
{ {
if (!priv::check(m_input)) if (!priv::check(m_input))
throw std::runtime_error("Bad input data for EmbossUpdateJob."); 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); m_result = priv::try_create_mesh(m_input, m_input.font_file, was_canceled);
if (was_canceled()) return; if (was_canceled()) return;
if (m_result.its.empty()) if (m_result.its.empty())
throw priv::EmbossJobException( throw priv::JobException(_u8L("Created text volume is empty. Change text or font.").c_str());
_u8L("Created text volume is empty. Change text or "
"font.").c_str());
// center triangle mesh // center triangle mesh
Vec3d shift = m_result.bounding_box().center(); Vec3d shift = m_result.bounding_box().center();
m_result.translate(-shift.cast<float>()); 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 // doesn't care about exception when process was canceled by user
if (canceled || m_input.cancel->load()) { 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); priv::update_volume(std::move(m_result), m_input);
} }
UseSurfaceData::ModelSources UseSurfaceData::create_sources( namespace Slic3r::GUI::Emboss {
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 {};
ModelSources result; SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> text_volume_id)
{
SurfaceVolumeData::ModelSources result;
result.reserve(volumes.size() - 1); result.reserve(volumes.size() - 1);
for (const ModelVolume *v : volumes) { 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, ... // skip modifiers and negative volumes, ...
if (!v->is_model_part()) continue; if (!v->is_model_part()) continue;
const TriangleMesh &tm = v->mesh(); const TriangleMesh &tm = v->mesh();
@ -340,15 +284,60 @@ UseSurfaceData::ModelSources UseSurfaceData::create_sources(
return result; 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 /// Create Surface volume
UseSurfaceJob::UseSurfaceJob(UseSurfaceData &&input) CreateSurfaceVolumeJob::CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input)
: m_input(std::move(input)) : m_input(std::move(input))
{ {
assert(priv::check(m_input, true)); 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)) if (!priv::check(m_input))
throw std::runtime_error("Bad input data for UseSurfaceJob."); throw std::runtime_error("Bad input data for UseSurfaceJob.");
@ -357,107 +346,25 @@ void UseSurfaceJob::process(Ctl &ctl) {
if (cancel->load()) return true; if (cancel->load()) return true;
return ctl.was_canceled(); return ctl.was_canceled();
}; };
m_result = priv::cut_surface(m_input, m_input, 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);
}
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 // doesn't care about exception when process was canceled by user
if (canceled || m_input.cancel->load()) { if (m_input.cancel->load()) {
eptr = nullptr; eptr = nullptr;
return; return;
} }
if (canceled) return;
if (priv::process(eptr)) return; if (priv::process(eptr)) return;
priv::update_volume(std::move(m_result), m_input); priv::update_volume(std::move(m_result), m_input);
} }
//////////////////////////// ////////////////////////////
/// private namespace implementation /// 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; bool res = true;
if (check_fontfile) { if (check_fontfile) {
assert(input.font_file.has_value()); 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(); res &= !input.text_configuration.text.empty();
assert(!input.volume_name.empty()); assert(!input.volume_name.empty());
res &= !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; 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 check_fontfile = false;
bool res = check((EmbossDataBase) input, check_fontfile); bool res = check((DataBase) input, check_fontfile);
assert(input.volume_type != ModelVolumeType::INVALID); assert(input.volume_type != ModelVolumeType::INVALID);
res &= input.volume_type != ModelVolumeType::INVALID; res &= input.volume_type != ModelVolumeType::INVALID;
assert(input.object_idx >= 0); assert(input.object_idx >= 0);
res &= input.object_idx >= 0; res &= input.object_idx >= 0;
if (is_main_thread) if (is_main_thread)
assert((size_t)input.object_idx < wxGetApp().model().objects.size()); 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; return res;
} }
bool priv::check(const EmbossDataCreateObject &input) { bool priv::check(const DataCreateObject &input) {
bool check_fontfile = false; bool check_fontfile = false;
bool res = check((EmbossDataBase) input, check_fontfile); bool res = check((DataBase) input, check_fontfile);
assert(input.screen_coor.x() >= 0.); assert(input.screen_coor.x() >= 0.);
res &= input.screen_coor.x() >= 0.; res &= input.screen_coor.x() >= 0.;
assert(input.screen_coor.y() >= 0.); assert(input.screen_coor.y() >= 0.);
@ -497,8 +402,9 @@ bool priv::check(const EmbossDataCreateObject &input) {
res &= input.bed_shape.size() >= 3; res &= input.bed_shape.size() >= 3;
return res; return res;
} }
bool priv::check(const EmbossDataUpdate &input, bool is_main_thread){ bool priv::check(const DataUpdate &input, bool is_main_thread, bool use_surface){
bool res = check((EmbossDataBase) input); bool check_fontfile = true;
bool res = check((DataBase) input, check_fontfile, use_surface);
assert(input.volume_id.id >= 0); assert(input.volume_id.id >= 0);
res &= input.volume_id.id >= 0; res &= input.volume_id.id >= 0;
if (is_main_thread) if (is_main_thread)
@ -509,15 +415,24 @@ bool priv::check(const EmbossDataUpdate &input, bool is_main_thread){
assert(!input.cancel->load()); assert(!input.cancel->load());
return res; return res;
} }
bool priv::check(const UseSurfaceData &input, bool is_main_thread){ bool priv::check(const CreateSurfaceVolumeData &input, bool is_main_thread)
bool res = check((EmbossDataUpdate) input, 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()); assert(!input.sources.empty());
res &= !input.sources.empty(); res &= !input.sources.empty();
return res; return res;
} }
template<typename Fnc> 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 TextConfiguration &tc = input.text_configuration;
const char *text = tc.text.c_str(); 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()); assert(font.has_value());
if (!font.has_value()) return {}; 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 (shapes.empty()) return {};
if (was_canceled()) 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; int unit_per_em = font.font_file->infos[font_index].unit_per_em;
float scale = prop.size_in_mm / unit_per_em; float scale = prop.size_in_mm / unit_per_em;
float depth = prop.emboss / scale; float depth = prop.emboss / scale;
auto projectZ = std::make_unique<Emboss::ProjectZ>(depth); auto projectZ = std::make_unique<ProjectZ>(depth);
Emboss::ProjectScale project(std::move(projectZ), scale); ProjectScale project(std::move(projectZ), scale);
if (was_canceled()) return {}; if (was_canceled()) return {};
return TriangleMesh(Emboss::polygons2model(shapes, project)); return TriangleMesh(polygons2model(shapes, project));
} }
template<typename Fnc> 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 // It is neccessary to create some shape
// Emboss text window is opened by creation new emboss text object // Emboss text window is opened by creation new emboss text object
@ -579,10 +494,10 @@ TriangleMesh priv::create_default_mesh()
return triangle_mesh; return triangle_mesh;
} }
void EmbossUpdateJob::update_volume(ModelVolume *volume, void UpdateJob::update_volume(ModelVolume *volume,
TriangleMesh &&mesh, TriangleMesh &&mesh,
const TextConfiguration &text_configuration, const TextConfiguration &text_configuration,
const std::string &volume_name) const std::string &volume_name)
{ {
// check inputs // check inputs
bool is_valid_input = bool is_valid_input =
@ -626,7 +541,7 @@ void EmbossUpdateJob::update_volume(ModelVolume *volume,
canvas->reload_scene(refresh_immediately); 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 // for sure that some object will be created
if (mesh.its.empty()) 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()) if (tc.has_value() && tc->fix_3mf_tr.has_value())
volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse()); 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, ModelVolume *priv::get_volume(ModelObjectPtrs &objects,
@ -664,7 +644,7 @@ ModelVolume *priv::get_volume(ModelObjectPtrs &objects,
return nullptr; return nullptr;
}; };
Emboss::OrthoProject priv::create_projection_for_cut( OrthoProject priv::create_projection_for_cut(
Transform3d tr, Transform3d tr,
double shape_scale, double shape_scale,
const std::pair<float, float> &z_range) 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 // Projection is in direction from far plane
tr.translate(Vec3d(0., 0., min_z)); tr.translate(Vec3d(0., 0., min_z));
tr.scale(shape_scale); 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) bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut)
{ {
// Offset of clossed side to model // Offset of clossed side to model
@ -699,14 +679,98 @@ Emboss::OrthoProject3d priv::create_emboss_projection(
back_move = -((is_outside) ? surface_offset : emboss); back_move = -((is_outside) ? surface_offset : emboss);
its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move))); its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move)));
Vec3d from_front_to_back(0., 0., back_move - 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) { bool priv::process(std::exception_ptr &eptr) {
if (!eptr) return false; if (!eptr) return false;
try { try {
std::rethrow_exception(eptr); std::rethrow_exception(eptr);
} catch (priv::EmbossJobException &e) { } catch (priv::JobException &e) {
create_message(e.what()); create_message(e.what());
eptr = nullptr; eptr = nullptr;
} }

View File

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

View File

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

View File

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

View File

@ -51,7 +51,7 @@ public:
/// <param name="font">wx descriptor of font</param> /// <param name="font">wx descriptor of font</param>
/// <param name="font_file">file described in wx font</param> /// <param name="font_file">file described in wx font</param>
/// <returns>New created font fileon success otherwise nullptr</returns> /// <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> /// <summary>
/// Set boldness into wx font /// Set boldness into wx font
@ -61,7 +61,7 @@ public:
/// <param name="font">wx descriptor of font</param> /// <param name="font">wx descriptor of font</param>
/// <param name="font_file">file described in wx font</param> /// <param name="font_file">file described in wx font</param>
/// <returns>New created font fileon success otherwise nullptr</returns> /// <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 // convert wxFont types to string and vice versa
static const boost::bimap<wxFontFamily, std::string_view> type_to_family; static const boost::bimap<wxFontFamily, std::string_view> type_to_family;