Cut surface in backend job

Store flag about use surface(into 3mf and style)
GUI: Add checkbox to start using model surface
This commit is contained in:
Filip Sykala 2022-05-04 19:42:39 +02:00
parent e5bf946008
commit 864d1e5830
13 changed files with 583 additions and 413 deletions

View File

@ -152,7 +152,7 @@ struct IntersectingElement
/// <returns>CGAL mesh - half edge mesh</returns>
CutMesh to_cgal(const indexed_triangle_set &its);
using Project = Emboss::IProject;
using Project = Emboss::IProjection;
/// <summary>
/// Covert 2d shape (e.g. Glyph) to CGAL model
/// </summary>
@ -434,9 +434,9 @@ void store(const SurfaceCuts &cut, const std::string &dir);
} // namespace privat
SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
SurfaceCut Slic3r::cut_surface(const indexed_triangle_set &model,
const ExPolygons &shapes,
const Emboss::IProject &projection)
const Emboss::IProjection &projection)
{
priv::CutMesh cgal_model = priv::to_cgal(model);
@ -519,12 +519,11 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
priv::store(result, DEBUG_OUTPUT_DIR + "cuts/"); // only debug
#endif // DEBUG_OUTPUT_DIR
// TODO: Filter surfaceCuts to only the top most.
return result;
return merge(std::move(result));
}
indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts,
const Emboss::IProject &projection)
indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts,
const Emboss::IProject3f &projection)
{
indexed_triangle_set result;
size_t count_vertices = 0;
@ -604,8 +603,8 @@ indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts,
return result;
}
indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut,
const Emboss::IProject &projection)
indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut,
const Emboss::IProject3f &projection)
{
assert(!cut.empty());
size_t count_vertices = cut.vertices.size() * 2;
@ -707,7 +706,7 @@ priv::CutMesh priv::to_cgal(const ExPolygons &shapes,
indices.reserve(polygon.points.size() * 2);
size_t num_vertices_old = result.number_of_vertices();
for (const Point &p2 : polygon.points) {
auto p = projection.project(p2);
auto p = projection.create_front_back(p2);
CutMesh::Point v_first{p.first.x(), p.first.y(), p.first.z()};
CutMesh::Point v_second{p.second.x(), p.second.y(), p.second.z()};
@ -1388,7 +1387,7 @@ void priv::filter_cuts(CutAOIs &cuts,
// compare distances of vertices
Point p = get_point(*i);
Vec3f source_point = projection.project(p).first;
Vec3f source_point = projection.create_front_back(p).first;
const auto &prev = mesh.point(ci.vi);
Vec3f prev_point(prev.x(), prev.y(), prev.z());
float prev_sq_norm = (source_point - prev_point).squaredNorm();

View File

@ -57,9 +57,9 @@ SurfaceCut merge(SurfaceCuts&& cuts);
/// <param name="shapes">Multiple shape to cut from model</param>
/// <param name="projection">Define transformation from 2d coordinate of shape to 3d</param>
/// <returns>Cutted surface from model</returns>
SurfaceCuts cut_surface(const indexed_triangle_set &model,
const ExPolygons &shapes,
const Emboss::IProject &projection);
SurfaceCut cut_surface(const indexed_triangle_set &model,
const ExPolygons &shapes,
const Emboss::IProjection &projection);
/// <summary>
/// Create model from surface cuts by projection
@ -67,16 +67,16 @@ SurfaceCuts cut_surface(const indexed_triangle_set &model,
/// <param name="cuts">Surfaces from model</param>
/// <param name="projection">Way of emboss</param>
/// <returns>Mesh</returns>
indexed_triangle_set cuts2model(const SurfaceCuts &cuts,
const Emboss::IProject &projection);
indexed_triangle_set cuts2model(const SurfaceCuts &cuts,
const Emboss::IProject3f &projection);
/// <summary>
/// Create model from surface cuts by projection
/// </summary>
/// <param name="cut">Surface from model with outlines</param>
/// <param name="projection">Way of emboss</param>
/// <returns>Mesh</returns>
indexed_triangle_set cut2model(const SurfaceCut &cut,
const Emboss::IProject &projection);
indexed_triangle_set cut2model(const SurfaceCut &cut,
const Emboss::IProject3f &projection);
} // namespace Slic3r

View File

@ -769,7 +769,7 @@ std::string Emboss::create_range_text(const std::string &text,
indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
const IProject &projection)
const IProjection &projection)
{
indexed_triangle_set result;
size_t count_point = count_points(shape2d);
@ -782,7 +782,7 @@ indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
auto insert_point = [&projection, &front_points,
&back_points](const Polygon& polygon) {
for (const Point& p : polygon.points) {
auto p2 = projection.project(p);
auto p2 = projection.create_front_back(p);
front_points.emplace_back(p2.first);
back_points.emplace_back(p2.second);
}
@ -832,7 +832,7 @@ indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
return result;
}
std::pair<Vec3f, Vec3f> Emboss::ProjectZ::project(const Point &p) const
std::pair<Vec3f, Vec3f> Emboss::ProjectZ::create_front_back(const Point &p) const
{
Vec3f front(
static_cast<float>(p.x() * SHAPE_SCALE),
@ -902,7 +902,7 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
// OrthoProject
std::pair<Vec3f, Vec3f> Emboss::OrthoProject::project(const Point &p) const {
std::pair<Vec3f, Vec3f> Emboss::OrthoProject::create_front_back(const Point &p) const {
Vec3d front(p.x(), p.y(), 0.);
Vec3f front_tr = (m_matrix * front).cast<float>();
return std::make_pair(front_tr, project(front_tr));

View File

@ -183,23 +183,12 @@ public:
static std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr);
/// <summary>
/// Project 2d point into space
/// Could be plane, sphere, cylindric, ...
/// Project spatial point
/// </summary>
class IProject
class IProject3f
{
public:
virtual ~IProject() = default;
/// <summary>
/// convert 2d point to 3d point
/// </summary>
/// <param name="p">2d coordinate</param>
/// <returns>
/// first - front spatial point
/// second - back spatial point
/// </returns>
virtual std::pair<Vec3f, Vec3f> project(const Point &p) const = 0;
virtual ~IProject3f() = default;
/// <summary>
/// Move point with respect to projection direction
/// e.g. Orthogonal projection will move with point by direction
@ -210,13 +199,33 @@ public:
virtual Vec3f project(const Vec3f &point) const = 0;
};
/// <summary>
/// Project 2d point into space
/// Could be plane, sphere, cylindric, ...
/// </summary>
class IProjection : public IProject3f
{
public:
virtual ~IProjection() = default;
/// <summary>
/// convert 2d point to 3d points
/// </summary>
/// <param name="p">2d coordinate</param>
/// <returns>
/// first - front spatial point
/// second - back spatial point
/// </returns>
virtual std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const = 0;
};
/// <summary>
/// Create triangle model for text
/// </summary>
/// <param name="shape2d">text or image</param>
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
/// <returns>Projected shape into space</returns>
static indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProject& projection);
static indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
/// <summary>
/// Create transformation for emboss text object to lay on surface point
@ -228,29 +237,29 @@ public:
static Transform3d create_transformation_onto_surface(
const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f);
class ProjectZ : public IProject
class ProjectZ : public IProjection
{
public:
ProjectZ(float depth) : m_depth(depth) {}
// Inherited via IProject
std::pair<Vec3f, Vec3f> project(const Point &p) const override;
std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override;
Vec3f project(const Vec3f &point) const override;
float m_depth;
};
class ProjectScale : public IProject
class ProjectScale : public IProjection
{
std::unique_ptr<IProject> core;
std::unique_ptr<IProjection> core;
float m_scale;
public:
ProjectScale(std::unique_ptr<IProject> core, float scale)
ProjectScale(std::unique_ptr<IProjection> core, float scale)
: core(std::move(core)), m_scale(scale)
{}
// Inherited via IProject
std::pair<Vec3f, Vec3f> project(const Point &p) const override
std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override
{
auto res = core->project(p);
auto res = core->create_front_back(p);
return std::make_pair(res.first * m_scale, res.second * m_scale);
}
Vec3f project(const Vec3f &point) const override{
@ -258,7 +267,16 @@ public:
}
};
class OrthoProject: public Emboss::IProject {
class OrthoProject3f : public Emboss::IProject3f
{
// size and direction of emboss for ortho projection
Vec3f m_direction;
public:
OrthoProject3f(Vec3f direction) : m_direction(direction) {}
Vec3f project(const Vec3f &point) const override{ return point + m_direction;}
};
class OrthoProject: public Emboss::IProjection {
Transform3d m_matrix;
// size and direction of emboss for ortho projection
Vec3f m_direction;
@ -267,7 +285,7 @@ public:
: m_matrix(matrix), m_direction(direction)
{}
// Inherited via IProject
std::pair<Vec3f, Vec3f> project(const Point &p) const override;
std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override;
Vec3f project(const Vec3f &point) const override;
};
};

View File

@ -157,6 +157,7 @@ static constexpr const char *CHAR_GAP_ATTR = "char_gap";
static constexpr const char *LINE_GAP_ATTR = "line_gap";
static constexpr const char *LINE_HEIGHT_ATTR = "line_height";
static constexpr const char *DEPTH_ATTR = "depth";
static constexpr const char *USE_SURFACE_ATTR = "use_surface";
static constexpr const char *BOLDNESS_ATTR = "boldness";
static constexpr const char *SKEW_ATTR = "skew";
static constexpr const char *DISTANCE_ATTR = "distance";
@ -3335,6 +3336,8 @@ void TextConfigurationSerialization::to_xml(std::stringstream &stream, const Tex
stream << LINE_HEIGHT_ATTR << "=\"" << fp.size_in_mm << "\" ";
stream << DEPTH_ATTR << "=\"" << fp.emboss << "\" ";
if (fp.use_surface)
stream << USE_SURFACE_ATTR << "=\"" << 1 << "\" ";
if (fp.boldness.has_value())
stream << BOLDNESS_ATTR << "=\"" << *fp.boldness << "\" ";
if (fp.skew.has_value())
@ -3416,7 +3419,9 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
fp.skew = skew;
float distance = get_attribute_value_float(attributes, num_attributes, DISTANCE_ATTR);
if (std::fabs(distance) > std::numeric_limits<float>::epsilon())
fp.distance = distance;
fp.distance = distance;
std::string use_surface = get_attribute_value_string(attributes, num_attributes, USE_SURFACE_ATTR);
if (!use_surface.empty()) fp.use_surface = true;
float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR);
if (std::fabs(angle) > std::numeric_limits<float>::epsilon())
fp.angle = angle;

View File

@ -25,6 +25,12 @@ struct FontProp
// Z depth of text
float emboss; // [in mm]
// Flag that text should use surface cutted from object
// FontProp::distance should without value
// FontProp::emboss should be positive number
// Note: default value is false
bool use_surface;
// positive value mean wider character shape
// negative value mean tiner character shape
// When not set value is zero and is not stored
@ -85,13 +91,14 @@ struct FontProp
/// <param name="line_height">Y size of text [in mm]</param>
/// <param name="depth">Z size of text [in mm]</param>
FontProp(float line_height = 10.f, float depth = 2.f)
: emboss(depth), size_in_mm(line_height)
: emboss(depth), size_in_mm(line_height), use_surface(false)
{}
bool operator==(const FontProp& other) const {
return
char_gap == other.char_gap &&
line_gap == other.line_gap &&
use_surface == other.use_surface &&
is_approx(emboss, other.emboss) &&
is_approx(size_in_mm, other.size_in_mm) &&
is_approx(boldness, other.boldness) &&

View File

@ -603,13 +603,15 @@ void GLGizmoEmboss::initialize()
int count_letter_M_in_input = 12;
cfg.advanced_input_width = letter_m_size.x * count_letter_M_in_input;
GuiCfg::Translations &tr = cfg.translations;
tr.font = _u8L("Font");
tr.size = _u8L("Height");
tr.depth = _u8L("Depth");
tr.font = _u8L("Font");
tr.size = _u8L("Height");
tr.depth = _u8L("Depth");
tr.use_surface = _u8L("Use surface");
float max_edit_text_width = std::max(
{ImGui::CalcTextSize(tr.font.c_str()).x,
ImGui::CalcTextSize(tr.size.c_str()).x,
ImGui::CalcTextSize(tr.depth.c_str()).x });
ImGui::CalcTextSize(tr.depth.c_str()).x,
ImGui::CalcTextSize(tr.use_surface.c_str()).x });
cfg.edit_input_offset =
3 * space + ImGui::GetTreeNodeToLabelSpacing() +
max_edit_text_width;
@ -646,7 +648,7 @@ void GLGizmoEmboss::initialize()
float window_width = cfg.style_combobox_width + style.WindowPadding.x * 2;
cfg.minimal_window_size = ImVec2(window_width, window_height);
float addition_edit_height = input_height * 3 + tree_header;
float addition_edit_height = input_height * 4 + tree_header;
cfg.minimal_window_size_with_edit = ImVec2(cfg.minimal_window_size.x,
cfg.minimal_window_size.y +
addition_edit_height);
@ -869,11 +871,40 @@ bool GLGizmoEmboss::process()
m_update_job_cancel = std::make_shared<std::atomic<bool> >(false);
EmbossDataBase base{font, create_configuration(), create_volume_name()};
EmbossDataUpdate data{std::move(base), m_volume->id(), m_update_job_cancel};
std::unique_ptr<Job> job = nullptr;
// check cutting from source mesh
const TextConfiguration &tc = data.text_configuration;
if (tc.font_item.prop.use_surface) {
// Model to cut surface from.
const ModelVolume *mesh = get_volume_to_cut_surface_from();
if (mesh == nullptr) return false;
Transform3d text_tr = m_volume->get_matrix();
if (tc.fix_3mf_tr.has_value())
text_tr = text_tr * tc.fix_3mf_tr->inverse();
bool is_outside = m_volume->is_model_part();
// check that there is not unexpected volume type
assert(is_outside || m_volume->is_negative_volume() ||
m_volume->is_modifier());
UseSurfaceData surface_data{std::move(data),
text_tr,
is_outside,
mesh->mesh().its /*copy*/,
mesh->get_matrix(),
mesh->mesh().bounding_box()};
job = std::make_unique<UseSurfaceJob>(std::move(surface_data));
} else {
job = std::make_unique<EmbossUpdateJob>(std::move(data));
}
//*
auto &worker = wxGetApp().plater()->get_ui_job_worker();
queue_job(worker, std::make_unique<EmbossUpdateJob>(std::move(data)));
queue_job(worker, std::move(job));
/*/ // Run Job on main thread (blocking) - ONLY DEBUG
execute_job(std::make_shared<EmbossUpdateJob>(std::move(data)));
execute_job(std::move(job));
// */
// notification is removed befor object is changed by job
@ -913,49 +944,18 @@ void GLGizmoEmboss::select_stored_font_item()
m_stored_font_item = it->second;
}
/// <summary>
/// choose valid source object for cut surface
/// </summary>
/// <param name="text_volume">Source model volume</param>
/// <returns>triangle set OR nullptr</returns>
const indexed_triangle_set *get_source_object(const ModelVolume* mv) {
if (mv == nullptr) return nullptr;
if (!mv->text_configuration.has_value()) return nullptr;
const auto &volumes = mv->get_object()->volumes;
// no other volume in object
if (volumes.size() <= 1) return nullptr;
// Improve create object from part or use gl_volume
// Get first model part in object
for (const ModelVolume *v : volumes) {
if (v->id() == mv->id()) continue;
if (!v->is_model_part()) continue;
const TriangleMesh &tm = v->mesh();
if (tm.empty()) continue;
return &tm.its;
}
// No valid source volume in objct volumes
return nullptr;
}
/// <summary>
/// Choose valid source Volume to project on(cut surface from).
/// </summary>
/// <param name="text_volume">Volume with text</param>
/// <returns>ModelVolume to project on</returns>
const ModelVolume *get_source_volume(const ModelVolume *text_volume)
const ModelVolume * GLGizmoEmboss::get_volume_to_cut_surface_from()
{
if (text_volume == nullptr) return nullptr;
if (!text_volume->text_configuration.has_value()) return nullptr;
const auto &volumes = text_volume->get_object()->volumes;
if (m_volume == nullptr) return nullptr;
if (!m_volume->text_configuration.has_value()) return nullptr;
const auto &volumes = m_volume->get_object()->volumes;
// no other volume in object
if (volumes.size() <= 1) return nullptr;
// Improve create object from part or use gl_volume
// Get first model part in object
for (const ModelVolume *v : volumes) {
if (v->id() == text_volume->id()) continue;
if (v->id() == m_volume->id()) continue;
if (!v->is_model_part()) continue;
const TriangleMesh &tm = v->mesh();
if (tm.empty()) continue;
@ -967,169 +967,36 @@ const ModelVolume *get_source_volume(const ModelVolume *text_volume)
return nullptr;
}
double get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff)
{
const auto &cn = fp.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
int unit_per_em = ff.infos[font_index].unit_per_em;
double scale = fp.size_in_mm / unit_per_em;
// Shape is scaled for store point coordinate as integer
return scale * Emboss::SHAPE_SCALE;
}
/// <summary>
/// Create cut_projection for cut surface
/// </summary>
/// <param name="tr">Volume transformation in object</param>
/// <param name="tc">Configuration of embossig</param>
/// <param name="ff">Font file for size --> unit per em</param>
/// <param name="shape_bb">Bounding box 2d of shape to center result volume</param>
/// <param name="z_range">Bounding box 3d of model volume for projection ranges</param>
/// <returns>Orthogonal cut_projection</returns>
std::unique_ptr<Emboss::IProject> create_projection_for_cut(
Transform3d tr,
const TextConfiguration &tc,
const Emboss::FontFile &ff,
const BoundingBox &shape_bb,
const std::pair<float, float>& z_range)
{
// create sure that emboss object is bigger than source object
const float safe_extension = 1.0f;
float min_z = z_range.first - safe_extension;
float max_z = z_range.second + safe_extension;
assert(min_z < max_z);
// range between min and max value
double projection_size = max_z - min_z;
Matrix3d transformation_for_vector = tr.linear();
// Projection must be negative value.
// System of text coordinate
// X .. from left to right
// Y .. from bottom to top
// Z .. from text to eye
Vec3d untransformed_direction(0., 0., projection_size);
Vec3f project_direction =
(transformation_for_vector * untransformed_direction).cast<float>();
// Projection is in direction from far plane
tr.translate(Vec3d(0., 0., min_z));
tr.scale(get_shape_scale(tc.font_item.prop, ff));
// Text alignemnt to center 2D
Vec2d move = -(shape_bb.max + shape_bb.min).cast<double>() / 2.;
tr.translate(Vec3d(move.x(), move.y(), 0.));
return std::make_unique<Emboss::OrthoProject>(tr, project_direction);
}
#include "libslic3r/CutSurface.hpp"
/// <summary>
/// Create tranformation for emboss
/// </summary>
/// <param name="is_outside">True .. raise, False .. engrave</param>
/// <param name="tc">Text configuration</param>
/// <param name="tr">Text voliume transformation inside object</param>
/// <param name="cut">Cutted surface from model</param>
/// <returns>Projection</returns>
static std::unique_ptr<Emboss::IProject> create_emboss_projection(
bool is_outside,
const TextConfiguration &tc,
Transform3d tr,
SurfaceCut &cut)
{
// Offset of clossed side to model
const float surface_offset = 1e-3f; // [in mm]
const FontProp &fp = tc.font_item.prop;
float front_move, back_move;
if (is_outside) {
front_move = fp.emboss;
back_move = -surface_offset;
} else {
front_move = surface_offset;
back_move = -fp.emboss;
}
Matrix3d rot_i = tr.linear().inverse();
its_transform(cut, rot_i);
BoundingBoxf3 bb = Slic3r::bounding_box(cut);
float z_move = -bb.max.z();
float x_center = (bb.max.x() + bb.min.x()) / 2;
float y_center = (bb.max.y() + bb.min.y()) / 2;
// move to front distance
Vec3f move(x_center, y_center, front_move + z_move);
its_translate(cut, move);
Vec3f from_front_to_back(0.f, 0.f, back_move - front_move);
return std::make_unique<Emboss::OrthoProject>(Transform3d::Identity() /*not used*/,
from_front_to_back);
}
void GLGizmoEmboss::use_surface() {
// Model to cut surface from.
const ModelVolume *source = get_source_volume(m_volume);
if (source == nullptr) return;
// font face with glyph cache
auto ffc = m_font_manager.get_font().font_file_with_cache;
if (!ffc.has_value()) return;
// Model to cut surface from.
const ModelVolume *mesh = get_volume_to_cut_surface_from();
if (mesh == nullptr) return;
// Cancel previous job
if (m_update_job_cancel != nullptr) m_update_job_cancel->store(true);
// create new shared ptr to cancel new job
m_update_job_cancel = std::make_shared<std::atomic<bool>>(false);
EmbossDataBase base{ffc, create_configuration(), create_volume_name()};
EmbossDataUpdate data{std::move(base), m_volume->id(), m_update_job_cancel};
assert(m_volume->text_configuration.has_value());
if (!m_volume->text_configuration.has_value()) return;
const TextConfiguration &tc = *m_volume->text_configuration;
const char *text = tc.text.c_str();
const FontProp& fp = tc.font_item.prop;
ExPolygons shapes = Emboss::text2shapes(ffc, text, fp);
if (shapes.empty()) return;
if (shapes.front().contour.empty()) return;
BoundingBox bb = get_extents(shapes);
Transform3d text_tr = m_volume->get_matrix();
if (tc.fix_3mf_tr.has_value())
text_tr = text_tr * tc.fix_3mf_tr->inverse();
Transform3d source_tr = source->get_matrix();
Transform3d source_tr_inv = source_tr.inverse();
Transform3d cut_projection_tr = source_tr_inv * text_tr;
//Transform3d cut_projection_tr = source_tr * text_tr.inverse();
BoundingBoxf3 source_bb = source->mesh().bounding_box();
BoundingBoxf3 source_bb_tr = source_bb.transformed(cut_projection_tr.inverse());
std::pair<float, float> z_range{source_bb_tr.min.z(), source_bb_tr.max.z()};
const Emboss::FontFile &ff = *ffc.font_file;
auto cut_projection = create_projection_for_cut(cut_projection_tr, tc, ff, bb, z_range);
if (cut_projection == nullptr) return;
SurfaceCuts cuts = cut_surface(source->mesh().its, shapes, *cut_projection);
if (cuts.empty()) return;
text_tr = text_tr * tc.fix_3mf_tr->inverse();
bool is_outside = m_volume->is_model_part();
// check that there is not unexpected volume type
assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier());
SurfaceCut cut = merge(std::move(cuts));
// !! Projection needs to transform cut
auto projection = create_emboss_projection(is_outside, tc, cut_projection_tr, cut);
if (projection == nullptr) return;
UseSurfaceData surface_data{std::move(data), text_tr, is_outside,
mesh->mesh().its /*copy*/, mesh->get_matrix(), mesh->mesh().bounding_box()};
indexed_triangle_set new_its = cut2model(cut, *projection);
its_write_obj(new_its, "C:/data/temp/projected.obj"); // only debug
TriangleMesh tm(std::move(new_its));
// center triangle mesh
Vec3d shift = tm.bounding_box().center();
// do not center in emboss direction
shift.z() = 0;
tm.translate(-shift.cast<float>());
// discard fix transformation when exists
if (tc.fix_3mf_tr.has_value()) {
m_volume->set_transformation(text_tr);
m_volume->text_configuration->fix_3mf_tr = {};
}
m_volume->set_mesh(std::move(tm));
m_volume->set_new_unique_id();
wxGetApp().plater()->canvas3D()->reload_scene(true);
auto &worker = wxGetApp().plater()->get_ui_job_worker();
queue_job(worker, std::make_unique<UseSurfaceJob>(std::move(surface_data)));
}
void GLGizmoEmboss::draw_window()
@ -1907,6 +1774,25 @@ bool GLGizmoEmboss::rev_input(const std::string &name,
return revertible(name, value, default_value, exist_change, undo_tooltip, undo_offset, draw_offseted_input);
}
bool GLGizmoEmboss::rev_checkbox(const std::string &name,
bool &value,
bool *default_value,
const std::string &undo_tooltip)
{
// draw offseted input
auto draw_offseted_input = [&]() -> bool {
float input_offset = m_gui_cfg->edit_input_offset;
ImGui::SameLine(input_offset);
return ImGui::Checkbox(("##" + name).c_str(), &value);
};
float undo_offset = ImGui::GetStyle().FramePadding.x;
bool exist_change = default_value != nullptr ?
(value != *default_value) :
false;
return revertible(name, value, default_value, exist_change, undo_tooltip,
undo_offset, draw_offseted_input);
}
void GLGizmoEmboss::draw_style_edit() {
const GuiCfg::Translations &tr = m_gui_cfg->translations;
@ -2006,6 +1892,18 @@ void GLGizmoEmboss::draw_style_edit() {
_u8L("Revert embossed depth."), 0.1f, 0.25, "%.2f mm"))
process();
bool *def_use_surface = m_stored_font_item.has_value() ?
&m_stored_font_item->prop.use_surface : nullptr;
if (rev_checkbox(tr.use_surface, font_prop.use_surface, def_use_surface,
_u8L("Revert using of model surface."))) {
if (font_prop.use_surface) {
font_prop.distance.reset();
if (font_prop.emboss < 0.1)
font_prop.emboss = 1;
}
process();
}
if (ImGui::TreeNode(_u8L("advanced").c_str())) {
if (!m_is_advanced_edit_style) {
set_minimal_window_size(true, true);

View File

@ -115,6 +115,12 @@ private:
// TODO: only for developing - remove it
void use_surface();
/// <summary>
/// Choose valid source Volume to project on(cut surface from).
/// </summary>
/// <returns>ModelVolume to project on</returns>
const ModelVolume *get_volume_to_cut_surface_from();
/// <summary>
/// Reversible input float with option to restor default value
/// TODO: make more general, static and move to ImGuiWrapper
@ -123,6 +129,7 @@ private:
bool rev_input(const std::string &name, float &value, float *default_value,
const std::string &undo_tooltip, float step, float step_fast, const char *format,
ImGuiInputTextFlags flags = 0);
bool rev_checkbox(const std::string &name, bool &value, bool* default_value, const std::string &undo_tooltip);
bool rev_slider(const std::string &name, std::optional<int>& value, std::optional<int> *default_value,
const std::string &undo_tooltip, int v_min, int v_max, const std::string &format, const wxString &tooltip);
bool rev_slider(const std::string &name, std::optional<float>& value, std::optional<float> *default_value,
@ -185,6 +192,7 @@ private:
std::string font;
std::string size;
std::string depth;
std::string use_surface;
// advanced
std::string char_gap;

View File

@ -2,6 +2,7 @@
#include <libslic3r/Model.hpp>
#include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh
#include <libslic3r/CutSurface.hpp> // use surface cuts
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/NotificationManager.hpp"
@ -40,105 +41,46 @@ static TriangleMesh create_mesh(const char *text,
/// </summary>
/// <returns>Not empty model(index trinagle set - its)</returns>
static TriangleMesh create_default_mesh();
/// <summary>
/// Must be called on main thread
/// </summary>
/// <param name="mesh">New mesh data</param>
/// <param name="data">Text configuration, ...</param>
static void update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data);
/// <summary>
/// Create projection for cut surface from mesh
/// </summary>
/// <param name="tr">Volume transformation in object</param>
/// <param name="tc">Configuration of embossig</param>
/// <param name="ff">Font file for size --> unit per em</param>
/// <param name="shape_bb">Bounding box 2d of shape to center result
/// volume</param> <param name="z_range">Bounding box 3d of model volume for
/// projection ranges</param> <returns>Orthogonal cut_projection</returns>
static std::unique_ptr<Emboss::IProjection> create_projection_for_cut(
Transform3d tr,
const TextConfiguration &tc,
const Emboss::FontFile &ff,
const BoundingBox &shape_bb,
const std::pair<float, float> &z_range);
/// <summary>
/// Create tranformation for emboss Cutted surface
/// </summary>
/// <param name="is_outside">True .. raise, False .. engrave</param>
/// <param name="tc">Text configuration</param>
/// <param name="tr">Text voliume transformation inside object</param>
/// <param name="cut">Cutted surface from model</param>
/// <returns>Projection</returns>
static std::unique_ptr<Emboss::IProject3f> create_emboss_projection(
bool is_outside,
const TextConfiguration &tc,
Transform3d tr,
SurfaceCut &cut);
}
/////////////////
/// Update Volume
EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input)
: m_input(std::move(input))
{
assert(m_input.cancel != nullptr);
assert(m_input.font_file.has_value());
assert(!m_input.text_configuration.text.empty());
assert(!m_input.text_configuration.fix_3mf_tr.has_value());
}
void EmbossUpdateJob::process(Ctl &ctl)
{
auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool {
if (cancel->load()) return true;
return ctl.was_canceled();
};
// check if exist valid font
if (!m_input.font_file.has_value()) return;
const TextConfiguration &cfg = m_input.text_configuration;
m_result = priv::create_mesh(cfg.text.c_str(), m_input.font_file,
cfg.font_item.prop, was_canceled);
if (m_result.its.empty()) return;
if (was_canceled()) return;
// center triangle mesh
Vec3d shift = m_result.bounding_box().center();
m_result.translate(-shift.cast<float>());
}
void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &)
{
if (canceled || m_input.cancel->load()) return;
// for sure that some object is created from shape
if (m_result.its.indices.empty()) return;
GUI_App & app = wxGetApp(); // may be move to input
Plater * plater = app.plater();
GLCanvas3D * canvas = plater->canvas3D();
// Check emboss gizmo is still open
GLGizmosManager &manager = canvas->get_gizmos_manager();
if (manager.get_current_type() != GLGizmosManager::Emboss) return;
std::string snap_name = GUI::format(_L("Text: %1%"), m_input.text_configuration.text);
Plater::TakeSnapshot snapshot(plater, snap_name, UndoRedo::SnapshotType::GizmoAction);
ModelVolume *volume = nullptr;
Model &model = plater->model();
for (auto obj : model.objects)
for (auto vol : obj->volumes)
if (vol->id() == m_input.volume_id) {
volume = vol;
break;
}
// could appear when user delete edited volume
if (volume == nullptr)
return;
// apply fix matrix made by store to .3mf
const auto &tc = volume->text_configuration;
assert(tc.has_value());
if (tc.has_value() && tc->fix_3mf_tr.has_value())
volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse());
// update volume
volume->set_mesh(std::move(m_result));
volume->set_new_unique_id();
volume->calculate_convex_hull();
volume->get_object()->invalidate_bounding_box();
volume->name = m_input.volume_name;
volume->text_configuration = m_input.text_configuration;
// update volume in right panel( volume / object name)
const Selection &selection = canvas->get_selection();
const GLVolume * gl_volume = selection.get_volume(
*selection.get_volume_idxs().begin());
int object_idx = gl_volume->object_idx();
int volume_idx = gl_volume->volume_idx();
ObjectList *obj_list = app.obj_list();
obj_list->update_name_in_list(object_idx, volume_idx);
// update printable state on canvas
if (volume->type() == ModelVolumeType::MODEL_PART)
canvas->update_instance_printable_state_for_object((size_t) object_idx);
// redraw scene
bool refresh_immediately = false;
canvas->reload_scene(refresh_immediately);
}
/////////////////
/// Create Volume
EmbossCreateVolumeJob::EmbossCreateVolumeJob(EmbossDataCreateVolume &&input)
@ -296,6 +238,104 @@ void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &)
canvas->reload_scene(true);
}
/////////////////
/// Update Volume
EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input)
: m_input(std::move(input))
{
assert(m_input.cancel != nullptr);
assert(m_input.font_file.has_value());
assert(!m_input.text_configuration.text.empty());
assert(!m_input.text_configuration.fix_3mf_tr.has_value());
}
void EmbossUpdateJob::process(Ctl &ctl)
{
auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool {
if (cancel->load()) return true;
return ctl.was_canceled();
};
// check if exist valid font
if (!m_input.font_file.has_value()) return;
const TextConfiguration &cfg = m_input.text_configuration;
m_result = priv::create_mesh(cfg.text.c_str(), m_input.font_file,
cfg.font_item.prop, was_canceled);
if (m_result.its.empty()) return;
if (was_canceled()) return;
// center triangle mesh
Vec3d shift = m_result.bounding_box().center();
m_result.translate(-shift.cast<float>());
}
void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &)
{
if (canceled || m_input.cancel->load()) return;
priv::update_volume(std::move(m_result), m_input);
}
/////////////////
/// Cut Surface
UseSurfaceJob::UseSurfaceJob(UseSurfaceData &&input)
: m_input(std::move(input))
{}
void UseSurfaceJob::process(Ctl &ctl) {
// font face with glyph cache
if (!m_input.font_file.has_value()) return;
// check cancelation of process
auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool {
if (cancel->load()) return true;
return ctl.was_canceled();
};
const TextConfiguration &tc = m_input.text_configuration;
const char *text = tc.text.c_str();
const FontProp &fp = tc.font_item.prop;
ExPolygons shapes = Emboss::text2shapes(m_input.font_file, text, fp);
if (shapes.empty()) return;
if (shapes.front().contour.empty()) return;
if (was_canceled()) return;
BoundingBox bb = get_extents(shapes);
Transform3d mesh_tr_inv = m_input.mesh_tr.inverse();
Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr;
BoundingBoxf3 mesh_bb_tr = m_input.mesh_bb.transformed(cut_projection_tr.inverse());
std::pair<float, float> z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()};
const Emboss::FontFile &ff = *m_input.font_file.font_file;
auto cut_projection = priv::create_projection_for_cut(cut_projection_tr, tc, ff, bb, z_range);
if (cut_projection == nullptr) return;
// Use CGAL to cut surface from triangle mesh
SurfaceCut cut = cut_surface(m_input.mesh_its, shapes, *cut_projection);
if (cut.empty()) return;
if (was_canceled()) return;
// !! Projection needs to transform cut
auto projection = priv::create_emboss_projection(m_input.is_outside, tc, cut_projection_tr, cut);
if (projection == nullptr) return;
indexed_triangle_set new_its = cut2model(cut, *projection);
if (was_canceled()) return;
//its_write_obj(new_its, "C:/data/temp/projected.obj"); // only debug
m_result = TriangleMesh(std::move(new_its));
Vec3d shift = -m_result.bounding_box().center();
// do not center in emboss direction
shift.z() = 0;
m_result.translate(shift.cast<float>());
}
void UseSurfaceJob::finalize(bool canceled, std::exception_ptr &)
{
if (canceled || m_input.cancel->load()) return;
priv::update_volume(std::move(m_result), m_input);
}
////////////////////////////
/// private namespace implementation
@ -334,4 +374,149 @@ TriangleMesh priv::create_default_mesh()
return TriangleMesh(its_make_cube(36., 4., 2.5));
}
return triangle_mesh;
}
}
static ModelVolume *get_volume(const ObjectID &volume_id, Model &model)
{
for (auto obj : model.objects)
for (auto vol : obj->volumes)
if (vol->id() == volume_id)
return vol;
return nullptr;
}
static ModelVolume *get_volume(const ObjectID &volume_id) {
return get_volume(volume_id, wxGetApp().plater()->model());
}
void priv::update_volume(TriangleMesh &&mesh,
const EmbossDataUpdate &data)
{
// for sure that some object will be created
if (mesh.its.empty()) return;
GUI_App & app = wxGetApp(); // may be move to input
Plater * plater = app.plater();
GLCanvas3D * canvas = plater->canvas3D();
// Check emboss gizmo is still open
GLGizmosManager &manager = canvas->get_gizmos_manager();
if (manager.get_current_type() != GLGizmosManager::Emboss) return;
std::string snap_name = GUI::format(_L("Text: %1%"), data.text_configuration.text);
Plater::TakeSnapshot snapshot(plater, snap_name, UndoRedo::SnapshotType::GizmoAction);
ModelVolume *volume = get_volume(data.volume_id, plater->model());
// could appear when user delete edited volume
if (volume == nullptr)
return;
// apply fix matrix made by store to .3mf
const auto &tc = volume->text_configuration;
assert(tc.has_value());
if (tc.has_value() && tc->fix_3mf_tr.has_value())
volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse());
// update volume
volume->set_mesh(std::move(mesh));
volume->set_new_unique_id();
volume->calculate_convex_hull();
volume->get_object()->invalidate_bounding_box();
volume->name = data.volume_name;
volume->text_configuration = data.text_configuration;
// update volume in right panel( volume / object name)
const Selection &selection = canvas->get_selection();
const GLVolume * gl_volume = selection.get_volume(
*selection.get_volume_idxs().begin());
int object_idx = gl_volume->object_idx();
int volume_idx = gl_volume->volume_idx();
ObjectList *obj_list = app.obj_list();
obj_list->update_name_in_list(object_idx, volume_idx);
// update printable state on canvas
if (volume->type() == ModelVolumeType::MODEL_PART)
canvas->update_instance_printable_state_for_object((size_t) object_idx);
// redraw scene
bool refresh_immediately = false;
canvas->reload_scene(refresh_immediately);
}
static double get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff)
{
const auto &cn = fp.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
int unit_per_em = ff.infos[font_index].unit_per_em;
double scale = fp.size_in_mm / unit_per_em;
// Shape is scaled for store point coordinate as integer
return scale * Emboss::SHAPE_SCALE;
}
std::unique_ptr<Emboss::IProjection> priv::create_projection_for_cut(
Transform3d tr,
const TextConfiguration &tc,
const Emboss::FontFile &ff,
const BoundingBox &shape_bb,
const std::pair<float, float> &z_range)
{
// create sure that emboss object is bigger than source object
const float safe_extension = 1.0f;
float min_z = z_range.first - safe_extension;
float max_z = z_range.second + safe_extension;
assert(min_z < max_z);
// range between min and max value
double projection_size = max_z - min_z;
Matrix3d transformation_for_vector = tr.linear();
// Projection must be negative value.
// System of text coordinate
// X .. from left to right
// Y .. from bottom to top
// Z .. from text to eye
Vec3d untransformed_direction(0., 0., projection_size);
Vec3f project_direction =
(transformation_for_vector * untransformed_direction).cast<float>();
// Projection is in direction from far plane
tr.translate(Vec3d(0., 0., min_z));
tr.scale(get_shape_scale(tc.font_item.prop, ff));
// Text alignemnt to center 2D
Vec2d move = -(shape_bb.max + shape_bb.min).cast<double>() / 2.;
tr.translate(Vec3d(move.x(), move.y(), 0.));
return std::make_unique<Emboss::OrthoProject>(tr, project_direction);
}
std::unique_ptr<Emboss::IProject3f> priv::create_emboss_projection(
bool is_outside,
const TextConfiguration &tc,
Transform3d tr,
SurfaceCut &cut)
{
// Offset of clossed side to model
const float surface_offset = 1e-3f; // [in mm]
const FontProp &fp = tc.font_item.prop;
float front_move, back_move;
if (is_outside) {
front_move = fp.emboss;
back_move = -surface_offset;
} else {
front_move = surface_offset;
back_move = -fp.emboss;
}
Matrix3d rot_i = tr.linear().inverse();
its_transform(cut, rot_i);
BoundingBoxf3 bb = Slic3r::bounding_box(cut);
float z_move = -bb.max.z();
float x_center = (bb.max.x() + bb.min.x()) / 2;
float y_center = (bb.max.y() + bb.min.y()) / 2;
// move to front distance
Vec3f move(x_center, y_center, front_move + z_move);
its_translate(cut, move);
Vec3f from_front_to_back(0.f, 0.f, back_move - front_move);
return std::make_unique<Emboss::OrthoProject3f>(from_front_to_back);
}

View File

@ -30,19 +30,6 @@ struct EmbossDataBase
std::string volume_name;
};
/// <summary>
/// Hold neccessary data to update embossed text object in job
/// </summary>
struct EmbossDataUpdate : public EmbossDataBase
{
// unique identifier of volume to change
ObjectID volume_id;
// flag that job is canceled
// for time after process.
std::shared_ptr<std::atomic<bool> > cancel;
};
/// <summary>
/// Hold neccessary data to create ModelVolume in job
/// Volume is created on the surface of existing volume in object.
@ -67,6 +54,22 @@ struct EmbossDataCreateVolume : public EmbossDataBase
Transform3d hit_instance_tr;
};
/// <summary>
/// Create new TextVolume on the surface of ModelObject
/// Should not be stopped
/// </summary>
class EmbossCreateVolumeJob : public Job
{
EmbossDataCreateVolume m_input;
TriangleMesh m_result;
Transform3d m_transformation;
public:
EmbossCreateVolumeJob(EmbossDataCreateVolume&& input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &) override;
};
/// <summary>
/// Hold neccessary data to create ModelObject in job
/// Object is placed on bed under screen coor
@ -84,51 +87,6 @@ struct EmbossDataCreateObject : public EmbossDataBase
std::vector<Vec2d> bed_shape;
};
/// <summary>
/// Update text shape in existing text volume
/// Predict that there is only one runnig(not canceled) instance of it
/// </summary>
class EmbossUpdateJob : public Job
{
EmbossDataUpdate m_input;
TriangleMesh m_result;
public:
// only move params to private variable
EmbossUpdateJob(EmbossDataUpdate&& input);
/// <summary>
/// Create volume movel by input data
/// </summary>
/// <param name="ctl">Control containing cancel flag</param>
void process(Ctl &ctl) override;
/// <summary>
/// Update volume - change object_id
/// </summary>
/// <param name="canceled">Was process canceled.
/// NOTE: Be carefull it doesn't care about
/// time between finished process and started finalize part.</param>
/// <param name="">unused</param>
void finalize(bool canceled, std::exception_ptr &) override;
};
/// <summary>
/// Create new TextVolume on the surface of ModelObject
/// Should not be stopped
/// </summary>
class EmbossCreateVolumeJob : public Job
{
EmbossDataCreateVolume m_input;
TriangleMesh m_result;
Transform3d m_transformation;
public:
EmbossCreateVolumeJob(EmbossDataCreateVolume&& input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &) override;
};
/// <summary>
/// Create new TextObject on the platter
/// Should not be stopped
@ -138,13 +96,91 @@ class EmbossCreateObjectJob : public Job
EmbossDataCreateObject m_input;
TriangleMesh m_result;
Transform3d m_transformation;
public:
EmbossCreateObjectJob(EmbossDataCreateObject&& input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &) override;
};
/// <summary>
/// Hold neccessary data to update embossed text object in job
/// </summary>
struct EmbossDataUpdate : public EmbossDataBase
{
// unique identifier of volume to change
ObjectID volume_id;
// flag that job is canceled
// for time after process.
std::shared_ptr<std::atomic<bool>> cancel;
};
/// <summary>
/// Update text shape in existing text volume
/// Predict that there is only one runnig(not canceled) instance of it
/// </summary>
class EmbossUpdateJob : public Job
{
EmbossDataUpdate m_input;
TriangleMesh m_result;
public:
// move params to private variable
EmbossUpdateJob(EmbossDataUpdate &&input);
/// <summary>
/// Create new embossed volume by m_input data and store to m_result
/// </summary>
/// <param name="ctl">Control containing cancel flag</param>
void process(Ctl &ctl) override;
/// <summary>
/// Update volume - change object_id
/// </summary>
/// <param name="canceled">Was process canceled.
/// NOTE: Be carefull it doesn't care about
/// time between finished process and started finalize part.</param>
/// <param name="">unused</param>
void finalize(bool canceled, std::exception_ptr &) override;
};
/// <summary>
/// Hold neccessary data to update embossed text object in job
/// </summary>
struct UseSurfaceData : public EmbossDataUpdate
{
// Transformation of text volume inside of object
Transform3d text_tr;
// Define projection move
// True (raised) .. move outside from surface
// False (engraved).. move into object
bool is_outside;
// IMPROVE: copy of source mesh tringles
// copy could slow down on big meshes
indexed_triangle_set mesh_its;
// Transformation of volume inside of object
Transform3d mesh_tr;
// extract bounds for projection
BoundingBoxf3 mesh_bb;
};
/// <summary>
/// Update text volume to use surface from object
/// </summary>
class UseSurfaceJob : public Job
{
UseSurfaceData m_input;
TriangleMesh m_result;
public:
// move params to private variable
UseSurfaceJob(UseSurfaceData &&input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &) override;
};
} // namespace Slic3r::GUI
#endif // slic3r_EmbossJob_hpp_

View File

@ -10,6 +10,7 @@ const std::string FontListSerializable::APP_CONFIG_FONT_NAME = "name";
const std::string FontListSerializable::APP_CONFIG_FONT_DESCRIPTOR = "descriptor";
const std::string FontListSerializable::APP_CONFIG_FONT_LINE_HEIGHT = "line_height";
const std::string FontListSerializable::APP_CONFIG_FONT_DEPTH = "depth";
const std::string FontListSerializable::APP_CONFIG_FONT_USE_SURFACE = "use_surface";
const std::string FontListSerializable::APP_CONFIG_FONT_BOLDNESS = "boldness";
const std::string FontListSerializable::APP_CONFIG_FONT_SKEW = "skew";
const std::string FontListSerializable::APP_CONFIG_FONT_DISTANCE = "distance";
@ -23,6 +24,15 @@ std::string FontListSerializable::create_section_name(unsigned index)
return AppConfig::SECTION_FONT + ':' + std::to_string(index);
}
// check only existence of flag
bool FontListSerializable::read(const std::map<std::string, std::string>& section, const std::string& key, bool& value){
auto item = section.find(key);
if (item == section.end()) return false;
value = true;
return true;
}
#include "fast_float/fast_float.h"
bool FontListSerializable::read(const std::map<std::string, std::string>& section, const std::string& key, float& value){
auto item = section.find(key);
@ -92,6 +102,7 @@ std::optional<FontItem> FontListSerializable::load_font_item(
FontProp fp;
read(app_cfg_section, APP_CONFIG_FONT_LINE_HEIGHT, fp.size_in_mm);
read(app_cfg_section, APP_CONFIG_FONT_DEPTH, fp.emboss);
read(app_cfg_section, APP_CONFIG_FONT_USE_SURFACE, fp.use_surface);
read(app_cfg_section, APP_CONFIG_FONT_BOLDNESS, fp.boldness);
read(app_cfg_section, APP_CONFIG_FONT_SKEW, fp.skew);
read(app_cfg_section, APP_CONFIG_FONT_DISTANCE, fp.distance);
@ -115,6 +126,8 @@ void FontListSerializable::store_font_item(AppConfig & cfg,
const FontProp &fp = fi.prop;
cfg.set(section_name, APP_CONFIG_FONT_LINE_HEIGHT, std::to_string(fp.size_in_mm));
cfg.set(section_name, APP_CONFIG_FONT_DEPTH, std::to_string(fp.emboss));
if (fp.use_surface)
cfg.set(section_name, APP_CONFIG_FONT_USE_SURFACE, "true");
if (fp.boldness.has_value())
cfg.set(section_name, APP_CONFIG_FONT_BOLDNESS, std::to_string(*fp.boldness));
if (fp.skew.has_value())

View File

@ -21,6 +21,7 @@ class FontListSerializable
static const std::string APP_CONFIG_FONT_DESCRIPTOR;
static const std::string APP_CONFIG_FONT_LINE_HEIGHT;
static const std::string APP_CONFIG_FONT_DEPTH;
static const std::string APP_CONFIG_FONT_USE_SURFACE;
static const std::string APP_CONFIG_FONT_BOLDNESS;
static const std::string APP_CONFIG_FONT_SKEW;
static const std::string APP_CONFIG_FONT_DISTANCE;
@ -37,10 +38,11 @@ public:
private:
// TODO: move to app config like read from section
static bool read(const std::map<std::string, std::string>& section, const std::string& key, bool& value);
static bool read(const std::map<std::string, std::string>& section, const std::string& key, float& value);
static bool read(const std::map<std::string, std::string>& section, const std::string& key, std::optional<int>& value);
static bool read(const std::map<std::string, std::string>& section, const std::string& key, std::optional<unsigned int>& value);
static bool read(const std::map<std::string, std::string>& section, const std::string& key, std::optional<float>& value);
static bool read(const std::map<std::string, std::string>& section, const std::string& key, std::optional<float>& value);
};
} // namespace Slic3r

View File

@ -330,10 +330,9 @@ TEST_CASE("Cut surface", "[]")
CHECK(!surfaces.empty());
Emboss::OrthoProject projection(Transform3d::Identity(), Vec3f(0.f, 0.f, 10.f));
for (auto &surface : surfaces)
its_translate(surface, Vec3f(0.f, 0.f, 10));
its_translate(surfaces, Vec3f(0.f, 0.f, 10));
indexed_triangle_set its = cuts2model(surfaces, projection);
indexed_triangle_set its = cut2model(surfaces, projection);
CHECK(!its.empty());
//its_write_obj(its, "C:/data/temp/projected.obj");
}
@ -479,12 +478,12 @@ namespace Slic3r::MeshBoolean::cgal2 {
/// <param name="face_shape_map_name">Name of property map to store conversion from face to contour</param>
/// <param name="contour_indices">Identify point on shape contour</param>
/// <returns>CGAL model of extruded shape</returns>
CGALMesh to_cgal(const ExPolygons &shape,
const Slic3r::Emboss::IProject &projection,
int32_t shape_id,
const std::string &edge_shape_map_name,
const std::string &face_shape_map_name,
std::vector<ShapesVertexId> &contour_indices)
CGALMesh to_cgal(const ExPolygons &shape,
const Slic3r::Emboss::IProjection &projection,
int32_t shape_id,
const std::string &edge_shape_map_name,
const std::string &face_shape_map_name,
std::vector<ShapesVertexId> &contour_indices)
{
CGALMesh result;
if (shape.empty()) return result;
@ -500,7 +499,7 @@ namespace Slic3r::MeshBoolean::cgal2 {
int32_t vertex_index = static_cast<int32_t>(contour_indices.size());
contour_indices.push_back({iexpoly, id, int32_t(num_vertices_old) });
for (const Point& p2 : polygon.points) {
auto p = projection.project(p2);
auto p = projection.create_front_back(p2);
auto vi = result.add_vertex(typename CGALMesh::Point{ p.first.x(), p.first.y(), p.first.z() });
assert((size_t)vi == indices.size() + num_vertices_old);
indices.emplace_back(vi);
@ -565,7 +564,7 @@ namespace Slic3r::MeshBoolean::cgal2 {
/// <returns>Cutted surface, Its do not represent Volume</returns>
indexed_triangle_set cut_shape(const indexed_triangle_set &source,
const ExPolygon &shape,
const Emboss::IProject &projection)
const Emboss::IProjection &projection)
{
// NOT implemented yet
return {};
@ -580,7 +579,7 @@ indexed_triangle_set cut_shape(const indexed_triangle_set &source,
/// <returns>Cutted surface, Its do not represent Volume</returns>
indexed_triangle_set cut_shape(const indexed_triangle_set &source,
const ExPolygons &shapes,
const Emboss::IProject &projection)
const Emboss::IProjection &projection)
{
indexed_triangle_set result;
for (const ExPolygon &shape : shapes)