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:
parent
e5bf946008
commit
864d1e5830
@ -152,7 +152,7 @@ struct IntersectingElement
|
|||||||
/// <returns>CGAL mesh - half edge mesh</returns>
|
/// <returns>CGAL mesh - half edge mesh</returns>
|
||||||
CutMesh to_cgal(const indexed_triangle_set &its);
|
CutMesh to_cgal(const indexed_triangle_set &its);
|
||||||
|
|
||||||
using Project = Emboss::IProject;
|
using Project = Emboss::IProjection;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Covert 2d shape (e.g. Glyph) to CGAL model
|
/// Covert 2d shape (e.g. Glyph) to CGAL model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -434,9 +434,9 @@ void store(const SurfaceCuts &cut, const std::string &dir);
|
|||||||
} // namespace privat
|
} // namespace privat
|
||||||
|
|
||||||
|
|
||||||
SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
|
SurfaceCut Slic3r::cut_surface(const indexed_triangle_set &model,
|
||||||
const ExPolygons &shapes,
|
const ExPolygons &shapes,
|
||||||
const Emboss::IProject &projection)
|
const Emboss::IProjection &projection)
|
||||||
{
|
{
|
||||||
priv::CutMesh cgal_model = priv::to_cgal(model);
|
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
|
priv::store(result, DEBUG_OUTPUT_DIR + "cuts/"); // only debug
|
||||||
#endif // DEBUG_OUTPUT_DIR
|
#endif // DEBUG_OUTPUT_DIR
|
||||||
|
|
||||||
// TODO: Filter surfaceCuts to only the top most.
|
return merge(std::move(result));
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts,
|
indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts,
|
||||||
const Emboss::IProject &projection)
|
const Emboss::IProject3f &projection)
|
||||||
{
|
{
|
||||||
indexed_triangle_set result;
|
indexed_triangle_set result;
|
||||||
size_t count_vertices = 0;
|
size_t count_vertices = 0;
|
||||||
@ -605,7 +604,7 @@ indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut,
|
indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut,
|
||||||
const Emboss::IProject &projection)
|
const Emboss::IProject3f &projection)
|
||||||
{
|
{
|
||||||
assert(!cut.empty());
|
assert(!cut.empty());
|
||||||
size_t count_vertices = cut.vertices.size() * 2;
|
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);
|
indices.reserve(polygon.points.size() * 2);
|
||||||
size_t num_vertices_old = result.number_of_vertices();
|
size_t num_vertices_old = result.number_of_vertices();
|
||||||
for (const Point &p2 : polygon.points) {
|
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_first{p.first.x(), p.first.y(), p.first.z()};
|
||||||
CutMesh::Point v_second{p.second.x(), p.second.y(), p.second.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
|
// compare distances of vertices
|
||||||
Point p = get_point(*i);
|
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);
|
const auto &prev = mesh.point(ci.vi);
|
||||||
Vec3f prev_point(prev.x(), prev.y(), prev.z());
|
Vec3f prev_point(prev.x(), prev.y(), prev.z());
|
||||||
float prev_sq_norm = (source_point - prev_point).squaredNorm();
|
float prev_sq_norm = (source_point - prev_point).squaredNorm();
|
||||||
|
@ -57,9 +57,9 @@ SurfaceCut merge(SurfaceCuts&& cuts);
|
|||||||
/// <param name="shapes">Multiple shape to cut from model</param>
|
/// <param name="shapes">Multiple shape to cut from model</param>
|
||||||
/// <param name="projection">Define transformation from 2d coordinate of shape to 3d</param>
|
/// <param name="projection">Define transformation from 2d coordinate of shape to 3d</param>
|
||||||
/// <returns>Cutted surface from model</returns>
|
/// <returns>Cutted surface from model</returns>
|
||||||
SurfaceCuts cut_surface(const indexed_triangle_set &model,
|
SurfaceCut cut_surface(const indexed_triangle_set &model,
|
||||||
const ExPolygons &shapes,
|
const ExPolygons &shapes,
|
||||||
const Emboss::IProject &projection);
|
const Emboss::IProjection &projection);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create model from surface cuts by projection
|
/// Create model from surface cuts by projection
|
||||||
@ -68,7 +68,7 @@ SurfaceCuts cut_surface(const indexed_triangle_set &model,
|
|||||||
/// <param name="projection">Way of emboss</param>
|
/// <param name="projection">Way of emboss</param>
|
||||||
/// <returns>Mesh</returns>
|
/// <returns>Mesh</returns>
|
||||||
indexed_triangle_set cuts2model(const SurfaceCuts &cuts,
|
indexed_triangle_set cuts2model(const SurfaceCuts &cuts,
|
||||||
const Emboss::IProject &projection);
|
const Emboss::IProject3f &projection);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create model from surface cuts by projection
|
/// Create model from surface cuts by projection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -76,7 +76,7 @@ indexed_triangle_set cuts2model(const SurfaceCuts &cuts,
|
|||||||
/// <param name="projection">Way of emboss</param>
|
/// <param name="projection">Way of emboss</param>
|
||||||
/// <returns>Mesh</returns>
|
/// <returns>Mesh</returns>
|
||||||
indexed_triangle_set cut2model(const SurfaceCut &cut,
|
indexed_triangle_set cut2model(const SurfaceCut &cut,
|
||||||
const Emboss::IProject &projection);
|
const Emboss::IProject3f &projection);
|
||||||
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -769,7 +769,7 @@ std::string Emboss::create_range_text(const std::string &text,
|
|||||||
|
|
||||||
|
|
||||||
indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
|
indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
|
||||||
const IProject &projection)
|
const IProjection &projection)
|
||||||
{
|
{
|
||||||
indexed_triangle_set result;
|
indexed_triangle_set result;
|
||||||
size_t count_point = count_points(shape2d);
|
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,
|
auto insert_point = [&projection, &front_points,
|
||||||
&back_points](const Polygon& polygon) {
|
&back_points](const Polygon& polygon) {
|
||||||
for (const Point& p : polygon.points) {
|
for (const Point& p : polygon.points) {
|
||||||
auto p2 = projection.project(p);
|
auto p2 = projection.create_front_back(p);
|
||||||
front_points.emplace_back(p2.first);
|
front_points.emplace_back(p2.first);
|
||||||
back_points.emplace_back(p2.second);
|
back_points.emplace_back(p2.second);
|
||||||
}
|
}
|
||||||
@ -832,7 +832,7 @@ indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
|
|||||||
return result;
|
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(
|
Vec3f front(
|
||||||
static_cast<float>(p.x() * SHAPE_SCALE),
|
static_cast<float>(p.x() * SHAPE_SCALE),
|
||||||
@ -902,7 +902,7 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
|
|||||||
|
|
||||||
// OrthoProject
|
// 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.);
|
Vec3d front(p.x(), p.y(), 0.);
|
||||||
Vec3f front_tr = (m_matrix * front).cast<float>();
|
Vec3f front_tr = (m_matrix * front).cast<float>();
|
||||||
return std::make_pair(front_tr, project(front_tr));
|
return std::make_pair(front_tr, project(front_tr));
|
||||||
|
@ -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);
|
static std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Project 2d point into space
|
/// Project spatial point
|
||||||
/// Could be plane, sphere, cylindric, ...
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class IProject
|
class IProject3f
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IProject() = default;
|
virtual ~IProject3f() = 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;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Move point with respect to projection direction
|
/// Move point with respect to projection direction
|
||||||
/// e.g. Orthogonal projection will move with point by direction
|
/// e.g. Orthogonal projection will move with point by direction
|
||||||
@ -210,13 +199,33 @@ public:
|
|||||||
virtual Vec3f project(const Vec3f &point) const = 0;
|
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>
|
/// <summary>
|
||||||
/// Create triangle model for text
|
/// Create triangle model for text
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <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 IProject& projection);
|
static 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
|
||||||
@ -228,29 +237,29 @@ public:
|
|||||||
static Transform3d create_transformation_onto_surface(
|
static 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 IProject
|
class ProjectZ : public IProjection
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ProjectZ(float depth) : m_depth(depth) {}
|
ProjectZ(float depth) : m_depth(depth) {}
|
||||||
// Inherited via IProject
|
// 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;
|
Vec3f project(const Vec3f &point) const override;
|
||||||
float m_depth;
|
float m_depth;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProjectScale : public IProject
|
class ProjectScale : public IProjection
|
||||||
{
|
{
|
||||||
std::unique_ptr<IProject> core;
|
std::unique_ptr<IProjection> core;
|
||||||
float m_scale;
|
float m_scale;
|
||||||
public:
|
public:
|
||||||
ProjectScale(std::unique_ptr<IProject> core, float scale)
|
ProjectScale(std::unique_ptr<IProjection> core, float scale)
|
||||||
: core(std::move(core)), m_scale(scale)
|
: core(std::move(core)), m_scale(scale)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// Inherited via IProject
|
// 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);
|
return std::make_pair(res.first * m_scale, res.second * m_scale);
|
||||||
}
|
}
|
||||||
Vec3f project(const Vec3f &point) const override{
|
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;
|
Transform3d m_matrix;
|
||||||
// size and direction of emboss for ortho projection
|
// size and direction of emboss for ortho projection
|
||||||
Vec3f m_direction;
|
Vec3f m_direction;
|
||||||
@ -267,7 +285,7 @@ public:
|
|||||||
: m_matrix(matrix), m_direction(direction)
|
: m_matrix(matrix), m_direction(direction)
|
||||||
{}
|
{}
|
||||||
// Inherited via IProject
|
// 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;
|
Vec3f project(const Vec3f &point) const override;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -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_GAP_ATTR = "line_gap";
|
||||||
static constexpr const char *LINE_HEIGHT_ATTR = "line_height";
|
static constexpr const char *LINE_HEIGHT_ATTR = "line_height";
|
||||||
static constexpr const char *DEPTH_ATTR = "depth";
|
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 *BOLDNESS_ATTR = "boldness";
|
||||||
static constexpr const char *SKEW_ATTR = "skew";
|
static constexpr const char *SKEW_ATTR = "skew";
|
||||||
static constexpr const char *DISTANCE_ATTR = "distance";
|
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 << LINE_HEIGHT_ATTR << "=\"" << fp.size_in_mm << "\" ";
|
||||||
stream << DEPTH_ATTR << "=\"" << fp.emboss << "\" ";
|
stream << DEPTH_ATTR << "=\"" << fp.emboss << "\" ";
|
||||||
|
if (fp.use_surface)
|
||||||
|
stream << USE_SURFACE_ATTR << "=\"" << 1 << "\" ";
|
||||||
if (fp.boldness.has_value())
|
if (fp.boldness.has_value())
|
||||||
stream << BOLDNESS_ATTR << "=\"" << *fp.boldness << "\" ";
|
stream << BOLDNESS_ATTR << "=\"" << *fp.boldness << "\" ";
|
||||||
if (fp.skew.has_value())
|
if (fp.skew.has_value())
|
||||||
@ -3417,6 +3420,8 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
|
|||||||
float distance = get_attribute_value_float(attributes, num_attributes, DISTANCE_ATTR);
|
float distance = get_attribute_value_float(attributes, num_attributes, DISTANCE_ATTR);
|
||||||
if (std::fabs(distance) > std::numeric_limits<float>::epsilon())
|
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);
|
float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR);
|
||||||
if (std::fabs(angle) > std::numeric_limits<float>::epsilon())
|
if (std::fabs(angle) > std::numeric_limits<float>::epsilon())
|
||||||
fp.angle = angle;
|
fp.angle = angle;
|
||||||
|
@ -25,6 +25,12 @@ struct FontProp
|
|||||||
// Z depth of text
|
// Z depth of text
|
||||||
float emboss; // [in mm]
|
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
|
// positive value mean wider character shape
|
||||||
// negative value mean tiner character shape
|
// negative value mean tiner character shape
|
||||||
// When not set value is zero and is not stored
|
// 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="line_height">Y size of text [in mm]</param>
|
||||||
/// <param name="depth">Z 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)
|
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 {
|
bool operator==(const FontProp& other) const {
|
||||||
return
|
return
|
||||||
char_gap == other.char_gap &&
|
char_gap == other.char_gap &&
|
||||||
line_gap == other.line_gap &&
|
line_gap == other.line_gap &&
|
||||||
|
use_surface == other.use_surface &&
|
||||||
is_approx(emboss, other.emboss) &&
|
is_approx(emboss, other.emboss) &&
|
||||||
is_approx(size_in_mm, other.size_in_mm) &&
|
is_approx(size_in_mm, other.size_in_mm) &&
|
||||||
is_approx(boldness, other.boldness) &&
|
is_approx(boldness, other.boldness) &&
|
||||||
|
@ -606,10 +606,12 @@ void GLGizmoEmboss::initialize()
|
|||||||
tr.font = _u8L("Font");
|
tr.font = _u8L("Font");
|
||||||
tr.size = _u8L("Height");
|
tr.size = _u8L("Height");
|
||||||
tr.depth = _u8L("Depth");
|
tr.depth = _u8L("Depth");
|
||||||
|
tr.use_surface = _u8L("Use surface");
|
||||||
float max_edit_text_width = std::max(
|
float max_edit_text_width = std::max(
|
||||||
{ImGui::CalcTextSize(tr.font.c_str()).x,
|
{ImGui::CalcTextSize(tr.font.c_str()).x,
|
||||||
ImGui::CalcTextSize(tr.size.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 =
|
cfg.edit_input_offset =
|
||||||
3 * space + ImGui::GetTreeNodeToLabelSpacing() +
|
3 * space + ImGui::GetTreeNodeToLabelSpacing() +
|
||||||
max_edit_text_width;
|
max_edit_text_width;
|
||||||
@ -646,7 +648,7 @@ void GLGizmoEmboss::initialize()
|
|||||||
float window_width = cfg.style_combobox_width + style.WindowPadding.x * 2;
|
float window_width = cfg.style_combobox_width + style.WindowPadding.x * 2;
|
||||||
cfg.minimal_window_size = ImVec2(window_width, window_height);
|
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_with_edit = ImVec2(cfg.minimal_window_size.x,
|
||||||
cfg.minimal_window_size.y +
|
cfg.minimal_window_size.y +
|
||||||
addition_edit_height);
|
addition_edit_height);
|
||||||
@ -869,11 +871,40 @@ bool GLGizmoEmboss::process()
|
|||||||
m_update_job_cancel = std::make_shared<std::atomic<bool> >(false);
|
m_update_job_cancel = std::make_shared<std::atomic<bool> >(false);
|
||||||
EmbossDataBase base{font, create_configuration(), create_volume_name()};
|
EmbossDataBase base{font, create_configuration(), create_volume_name()};
|
||||||
EmbossDataUpdate data{std::move(base), m_volume->id(), m_update_job_cancel};
|
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();
|
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
|
/*/ // 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
|
// 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;
|
m_stored_font_item = it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
const ModelVolume * GLGizmoEmboss::get_volume_to_cut_surface_from()
|
||||||
/// 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)
|
|
||||||
{
|
{
|
||||||
if (text_volume == nullptr) return nullptr;
|
if (m_volume == nullptr) return nullptr;
|
||||||
if (!text_volume->text_configuration.has_value()) return nullptr;
|
if (!m_volume->text_configuration.has_value()) return nullptr;
|
||||||
const auto &volumes = text_volume->get_object()->volumes;
|
const auto &volumes = m_volume->get_object()->volumes;
|
||||||
// no other volume in object
|
// no other volume in object
|
||||||
if (volumes.size() <= 1) return nullptr;
|
if (volumes.size() <= 1) return nullptr;
|
||||||
|
|
||||||
// Improve create object from part or use gl_volume
|
// Improve create object from part or use gl_volume
|
||||||
// Get first model part in object
|
// Get first model part in object
|
||||||
for (const ModelVolume *v : volumes) {
|
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;
|
if (!v->is_model_part()) continue;
|
||||||
const TriangleMesh &tm = v->mesh();
|
const TriangleMesh &tm = v->mesh();
|
||||||
if (tm.empty()) continue;
|
if (tm.empty()) continue;
|
||||||
@ -967,169 +967,36 @@ const ModelVolume *get_source_volume(const ModelVolume *text_volume)
|
|||||||
return nullptr;
|
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() {
|
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
|
// font face with glyph cache
|
||||||
auto ffc = m_font_manager.get_font().font_file_with_cache;
|
auto ffc = m_font_manager.get_font().font_file_with_cache;
|
||||||
if (!ffc.has_value()) return;
|
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 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();
|
Transform3d text_tr = m_volume->get_matrix();
|
||||||
if (tc.fix_3mf_tr.has_value())
|
if (tc.fix_3mf_tr.has_value())
|
||||||
text_tr = text_tr * tc.fix_3mf_tr->inverse();
|
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;
|
|
||||||
|
|
||||||
bool is_outside = m_volume->is_model_part();
|
bool is_outside = m_volume->is_model_part();
|
||||||
|
// check that there is not unexpected volume type
|
||||||
assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier());
|
assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier());
|
||||||
|
UseSurfaceData surface_data{std::move(data), text_tr, is_outside,
|
||||||
|
mesh->mesh().its /*copy*/, mesh->get_matrix(), mesh->mesh().bounding_box()};
|
||||||
|
|
||||||
SurfaceCut cut = merge(std::move(cuts));
|
auto &worker = wxGetApp().plater()->get_ui_job_worker();
|
||||||
// !! Projection needs to transform cut
|
queue_job(worker, std::make_unique<UseSurfaceJob>(std::move(surface_data)));
|
||||||
auto projection = create_emboss_projection(is_outside, tc, cut_projection_tr, cut);
|
|
||||||
if (projection == nullptr) return;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoEmboss::draw_window()
|
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);
|
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() {
|
void GLGizmoEmboss::draw_style_edit() {
|
||||||
const GuiCfg::Translations &tr = m_gui_cfg->translations;
|
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"))
|
_u8L("Revert embossed depth."), 0.1f, 0.25, "%.2f mm"))
|
||||||
process();
|
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 (ImGui::TreeNode(_u8L("advanced").c_str())) {
|
||||||
if (!m_is_advanced_edit_style) {
|
if (!m_is_advanced_edit_style) {
|
||||||
set_minimal_window_size(true, true);
|
set_minimal_window_size(true, true);
|
||||||
|
@ -115,6 +115,12 @@ private:
|
|||||||
// TODO: only for developing - remove it
|
// TODO: only for developing - remove it
|
||||||
void use_surface();
|
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>
|
/// <summary>
|
||||||
/// Reversible input float with option to restor default value
|
/// Reversible input float with option to restor default value
|
||||||
/// TODO: make more general, static and move to ImGuiWrapper
|
/// TODO: make more general, static and move to ImGuiWrapper
|
||||||
@ -123,6 +129,7 @@ private:
|
|||||||
bool rev_input(const std::string &name, float &value, float *default_value,
|
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,
|
const std::string &undo_tooltip, float step, float step_fast, const char *format,
|
||||||
ImGuiInputTextFlags flags = 0);
|
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,
|
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);
|
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,
|
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 font;
|
||||||
std::string size;
|
std::string size;
|
||||||
std::string depth;
|
std::string depth;
|
||||||
|
std::string use_surface;
|
||||||
|
|
||||||
// advanced
|
// advanced
|
||||||
std::string char_gap;
|
std::string char_gap;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <libslic3r/Model.hpp>
|
#include <libslic3r/Model.hpp>
|
||||||
#include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh
|
#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/Plater.hpp"
|
||||||
#include "slic3r/GUI/NotificationManager.hpp"
|
#include "slic3r/GUI/NotificationManager.hpp"
|
||||||
@ -40,105 +41,46 @@ static TriangleMesh create_mesh(const char *text,
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Not empty model(index trinagle set - its)</returns>
|
/// <returns>Not empty model(index trinagle set - its)</returns>
|
||||||
static TriangleMesh create_default_mesh();
|
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
|
/// Create Volume
|
||||||
EmbossCreateVolumeJob::EmbossCreateVolumeJob(EmbossDataCreateVolume &&input)
|
EmbossCreateVolumeJob::EmbossCreateVolumeJob(EmbossDataCreateVolume &&input)
|
||||||
@ -296,6 +238,104 @@ void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &)
|
|||||||
canvas->reload_scene(true);
|
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
|
/// private namespace implementation
|
||||||
@ -335,3 +375,148 @@ TriangleMesh priv::create_default_mesh()
|
|||||||
}
|
}
|
||||||
return triangle_mesh;
|
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);
|
||||||
|
}
|
||||||
|
@ -30,19 +30,6 @@ struct EmbossDataBase
|
|||||||
std::string volume_name;
|
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>
|
/// <summary>
|
||||||
/// Hold neccessary data to create ModelVolume in job
|
/// Hold neccessary data to create ModelVolume in job
|
||||||
/// Volume is created on the surface of existing volume in object.
|
/// Volume is created on the surface of existing volume in object.
|
||||||
@ -67,6 +54,22 @@ struct EmbossDataCreateVolume : public EmbossDataBase
|
|||||||
Transform3d hit_instance_tr;
|
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>
|
/// <summary>
|
||||||
/// Hold neccessary data to create ModelObject in job
|
/// Hold neccessary data to create ModelObject in job
|
||||||
/// Object is placed on bed under screen coor
|
/// Object is placed on bed under screen coor
|
||||||
@ -84,6 +87,34 @@ struct EmbossDataCreateObject : public EmbossDataBase
|
|||||||
std::vector<Vec2d> bed_shape;
|
std::vector<Vec2d> bed_shape;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create new TextObject on the platter
|
||||||
|
/// Should not be stopped
|
||||||
|
/// </summary>
|
||||||
|
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>
|
/// <summary>
|
||||||
/// 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
|
||||||
@ -92,12 +123,13 @@ class EmbossUpdateJob : public Job
|
|||||||
{
|
{
|
||||||
EmbossDataUpdate m_input;
|
EmbossDataUpdate m_input;
|
||||||
TriangleMesh m_result;
|
TriangleMesh m_result;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// only move params to private variable
|
// move params to private variable
|
||||||
EmbossUpdateJob(EmbossDataUpdate&& input);
|
EmbossUpdateJob(EmbossDataUpdate &&input);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create volume movel by input data
|
/// Create new embossed volume by m_input data and store to m_result
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ctl">Control containing cancel flag</param>
|
/// <param name="ctl">Control containing cancel flag</param>
|
||||||
void process(Ctl &ctl) override;
|
void process(Ctl &ctl) override;
|
||||||
@ -112,35 +144,39 @@ public:
|
|||||||
void finalize(bool canceled, std::exception_ptr &) override;
|
void finalize(bool canceled, std::exception_ptr &) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create new TextVolume on the surface of ModelObject
|
/// Hold neccessary data to update embossed text object in job
|
||||||
/// Should not be stopped
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class EmbossCreateVolumeJob : public Job
|
struct UseSurfaceData : public EmbossDataUpdate
|
||||||
{
|
{
|
||||||
EmbossDataCreateVolume m_input;
|
// Transformation of text volume inside of object
|
||||||
TriangleMesh m_result;
|
Transform3d text_tr;
|
||||||
Transform3d m_transformation;
|
|
||||||
|
|
||||||
public:
|
// Define projection move
|
||||||
EmbossCreateVolumeJob(EmbossDataCreateVolume&& input);
|
// True (raised) .. move outside from surface
|
||||||
void process(Ctl &ctl) override;
|
// False (engraved).. move into object
|
||||||
void finalize(bool canceled, std::exception_ptr &) override;
|
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>
|
/// <summary>
|
||||||
/// Create new TextObject on the platter
|
/// Update text volume to use surface from object
|
||||||
/// Should not be stopped
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class EmbossCreateObjectJob : public Job
|
class UseSurfaceJob : public Job
|
||||||
{
|
{
|
||||||
EmbossDataCreateObject m_input;
|
UseSurfaceData m_input;
|
||||||
TriangleMesh m_result;
|
TriangleMesh m_result;
|
||||||
Transform3d m_transformation;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EmbossCreateObjectJob(EmbossDataCreateObject&& input);
|
// move params to private variable
|
||||||
|
UseSurfaceJob(UseSurfaceData &&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;
|
||||||
};
|
};
|
||||||
|
@ -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_DESCRIPTOR = "descriptor";
|
||||||
const std::string FontListSerializable::APP_CONFIG_FONT_LINE_HEIGHT = "line_height";
|
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_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_BOLDNESS = "boldness";
|
||||||
const std::string FontListSerializable::APP_CONFIG_FONT_SKEW = "skew";
|
const std::string FontListSerializable::APP_CONFIG_FONT_SKEW = "skew";
|
||||||
const std::string FontListSerializable::APP_CONFIG_FONT_DISTANCE = "distance";
|
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);
|
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"
|
#include "fast_float/fast_float.h"
|
||||||
bool FontListSerializable::read(const std::map<std::string, std::string>& section, const std::string& key, float& value){
|
bool FontListSerializable::read(const std::map<std::string, std::string>& section, const std::string& key, float& value){
|
||||||
auto item = section.find(key);
|
auto item = section.find(key);
|
||||||
@ -92,6 +102,7 @@ std::optional<FontItem> FontListSerializable::load_font_item(
|
|||||||
FontProp fp;
|
FontProp fp;
|
||||||
read(app_cfg_section, APP_CONFIG_FONT_LINE_HEIGHT, fp.size_in_mm);
|
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_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_BOLDNESS, fp.boldness);
|
||||||
read(app_cfg_section, APP_CONFIG_FONT_SKEW, fp.skew);
|
read(app_cfg_section, APP_CONFIG_FONT_SKEW, fp.skew);
|
||||||
read(app_cfg_section, APP_CONFIG_FONT_DISTANCE, fp.distance);
|
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;
|
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_LINE_HEIGHT, std::to_string(fp.size_in_mm));
|
||||||
cfg.set(section_name, APP_CONFIG_FONT_DEPTH, std::to_string(fp.emboss));
|
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())
|
if (fp.boldness.has_value())
|
||||||
cfg.set(section_name, APP_CONFIG_FONT_BOLDNESS, std::to_string(*fp.boldness));
|
cfg.set(section_name, APP_CONFIG_FONT_BOLDNESS, std::to_string(*fp.boldness));
|
||||||
if (fp.skew.has_value())
|
if (fp.skew.has_value())
|
||||||
|
@ -21,6 +21,7 @@ class FontListSerializable
|
|||||||
static const std::string APP_CONFIG_FONT_DESCRIPTOR;
|
static const std::string APP_CONFIG_FONT_DESCRIPTOR;
|
||||||
static const std::string APP_CONFIG_FONT_LINE_HEIGHT;
|
static const std::string APP_CONFIG_FONT_LINE_HEIGHT;
|
||||||
static const std::string APP_CONFIG_FONT_DEPTH;
|
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_BOLDNESS;
|
||||||
static const std::string APP_CONFIG_FONT_SKEW;
|
static const std::string APP_CONFIG_FONT_SKEW;
|
||||||
static const std::string APP_CONFIG_FONT_DISTANCE;
|
static const std::string APP_CONFIG_FONT_DISTANCE;
|
||||||
@ -37,6 +38,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// TODO: move to app config like read from section
|
// 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, 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<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<unsigned int>& value);
|
||||||
|
@ -330,10 +330,9 @@ TEST_CASE("Cut surface", "[]")
|
|||||||
CHECK(!surfaces.empty());
|
CHECK(!surfaces.empty());
|
||||||
|
|
||||||
Emboss::OrthoProject projection(Transform3d::Identity(), Vec3f(0.f, 0.f, 10.f));
|
Emboss::OrthoProject projection(Transform3d::Identity(), Vec3f(0.f, 0.f, 10.f));
|
||||||
for (auto &surface : surfaces)
|
its_translate(surfaces, Vec3f(0.f, 0.f, 10));
|
||||||
its_translate(surface, Vec3f(0.f, 0.f, 10));
|
|
||||||
|
|
||||||
indexed_triangle_set its = cuts2model(surfaces, projection);
|
indexed_triangle_set its = cut2model(surfaces, projection);
|
||||||
CHECK(!its.empty());
|
CHECK(!its.empty());
|
||||||
//its_write_obj(its, "C:/data/temp/projected.obj");
|
//its_write_obj(its, "C:/data/temp/projected.obj");
|
||||||
}
|
}
|
||||||
@ -480,7 +479,7 @@ namespace Slic3r::MeshBoolean::cgal2 {
|
|||||||
/// <param name="contour_indices">Identify point on shape contour</param>
|
/// <param name="contour_indices">Identify point on shape contour</param>
|
||||||
/// <returns>CGAL model of extruded shape</returns>
|
/// <returns>CGAL model of extruded shape</returns>
|
||||||
CGALMesh to_cgal(const ExPolygons &shape,
|
CGALMesh to_cgal(const ExPolygons &shape,
|
||||||
const Slic3r::Emboss::IProject &projection,
|
const Slic3r::Emboss::IProjection &projection,
|
||||||
int32_t shape_id,
|
int32_t shape_id,
|
||||||
const std::string &edge_shape_map_name,
|
const std::string &edge_shape_map_name,
|
||||||
const std::string &face_shape_map_name,
|
const std::string &face_shape_map_name,
|
||||||
@ -500,7 +499,7 @@ namespace Slic3r::MeshBoolean::cgal2 {
|
|||||||
int32_t vertex_index = static_cast<int32_t>(contour_indices.size());
|
int32_t vertex_index = static_cast<int32_t>(contour_indices.size());
|
||||||
contour_indices.push_back({iexpoly, id, int32_t(num_vertices_old) });
|
contour_indices.push_back({iexpoly, id, int32_t(num_vertices_old) });
|
||||||
for (const Point& p2 : polygon.points) {
|
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() });
|
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);
|
assert((size_t)vi == indices.size() + num_vertices_old);
|
||||||
indices.emplace_back(vi);
|
indices.emplace_back(vi);
|
||||||
@ -565,7 +564,7 @@ namespace Slic3r::MeshBoolean::cgal2 {
|
|||||||
/// <returns>Cutted surface, Its do not represent Volume</returns>
|
/// <returns>Cutted surface, Its do not represent Volume</returns>
|
||||||
indexed_triangle_set cut_shape(const indexed_triangle_set &source,
|
indexed_triangle_set cut_shape(const indexed_triangle_set &source,
|
||||||
const ExPolygon &shape,
|
const ExPolygon &shape,
|
||||||
const Emboss::IProject &projection)
|
const Emboss::IProjection &projection)
|
||||||
{
|
{
|
||||||
// NOT implemented yet
|
// NOT implemented yet
|
||||||
return {};
|
return {};
|
||||||
@ -580,7 +579,7 @@ indexed_triangle_set cut_shape(const indexed_triangle_set &source,
|
|||||||
/// <returns>Cutted surface, Its do not represent Volume</returns>
|
/// <returns>Cutted surface, Its do not represent Volume</returns>
|
||||||
indexed_triangle_set cut_shape(const indexed_triangle_set &source,
|
indexed_triangle_set cut_shape(const indexed_triangle_set &source,
|
||||||
const ExPolygons &shapes,
|
const ExPolygons &shapes,
|
||||||
const Emboss::IProject &projection)
|
const Emboss::IProjection &projection)
|
||||||
{
|
{
|
||||||
indexed_triangle_set result;
|
indexed_triangle_set result;
|
||||||
for (const ExPolygon &shape : shapes)
|
for (const ExPolygon &shape : shapes)
|
||||||
|
Loading…
Reference in New Issue
Block a user