diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp
index 03637ab576..d8ef2071b7 100644
--- a/src/libslic3r/Emboss.cpp
+++ b/src/libslic3r/Emboss.cpp
@@ -1875,7 +1875,7 @@ PolygonPoints Emboss::sample_slice(const TextLine &slice, const BoundingBoxes &b
// find BB in center of line
size_t first_right_index = 0;
for (const BoundingBox &bb : bbs)
- if (bb.min.x() > 0) {
+ if (bb.min.x() >= 0) {
break;
} else {
++first_right_index;
diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp
index 5c10eae10a..fc0a1831d3 100644
--- a/src/libslic3r/Emboss.hpp
+++ b/src/libslic3r/Emboss.hpp
@@ -425,9 +425,9 @@ namespace Emboss
/// Sample slice polygon by bounding boxes centers
/// slice start point has shape_center_x coor
///
- /// Polygon and start point
- /// Bounding boxes of letter on one line
- /// Scale for bbs
+ /// Polygon and start point[Slic3r scaled milimeters]
+ /// Bounding boxes of letter on one line[in font scales]
+ /// Scale for bbs (after multiply bb is in milimeters)
/// Sampled polygon by bounding boxes
PolygonPoints sample_slice(const TextLine &slice, const BoundingBoxes &bbs, double scale);
diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp
index fc7d35131d..ca2f04327b 100644
--- a/src/slic3r/GUI/Jobs/EmbossJob.cpp
+++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp
@@ -42,6 +42,7 @@ bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
template static ExPolygons create_shape(DataBase &input, Fnc was_canceled);
+template static std::vector create_shapes(DataBase &input, Fnc was_canceled);
//
/// Try to create mesh from text
@@ -422,7 +423,7 @@ ExPolygons priv::create_shape(DataBase &input, Fnc was_canceled) {
const TextConfiguration &tc = input.text_configuration;
const char *text = tc.text.c_str();
const FontProp &prop = tc.style.prop;
-
+ assert(!prop.per_glyph);
assert(font.has_value());
if (!font.has_value())
return {};
@@ -435,37 +436,44 @@ ExPolygons priv::create_shape(DataBase &input, Fnc was_canceled) {
return shapes;
}
-#define STORE_SAMPLING
-#ifdef STORE_SAMPLING
-#include "libslic3r/SVG.hpp"
-#endif // STORE_SAMPLING
-namespace {
-template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc was_canceled)
-{
- // method use square of coord stored into int64_t
- static_assert(std::is_same());
-
+template
+std::vector priv::create_shapes(DataBase &input, Fnc was_canceled) {
FontFileWithCache &font = input.font_file;
const TextConfiguration &tc = input.text_configuration;
- assert(get_count_lines(tc.text) == input.text_lines.size());
- size_t count_lines = input.text_lines.size();
- std::wstring ws = boost::nowide::widen(tc.text.c_str());
- const FontProp &prop = tc.style.prop;
-
+ const char *text = tc.text.c_str();
+ const FontProp &prop = tc.style.prop;
+ assert(prop.per_glyph);
assert(font.has_value());
+ if (!font.has_value())
+ return {};
+
+ std::wstring ws = boost::nowide::widen(text);
std::vector shapes = text2vshapes(font, ws, prop, was_canceled);
if (shapes.empty())
return {};
- align_shape(prop.align, shapes);
+ align_shape(prop.align, shapes);
+ if (was_canceled())
+ return {};
- const FontFile &ff = *font.font_file;
- double shape_scale = get_shape_scale(prop, ff);
+ return shapes;
+}
- // Precalculate bounding boxes of glyphs
- // Separate lines of text to vector of Bounds
- std::vector bbs(count_lines);
- size_t text_line_index = 0;
+//#define STORE_SAMPLING
+#ifdef STORE_SAMPLING
+#include "libslic3r/SVG.hpp"
+#endif // STORE_SAMPLING
+namespace {
+
+std::vector create_line_bounds(const std::vector &shapes, const std::wstring& text, size_t count_lines = 0)
+{
+ assert(text.size() == shapes.size());
+ if (count_lines == 0)
+ count_lines = get_count_lines(text);
+ assert(count_lines == get_count_lines(text));
+
+ std::vector result(count_lines);
+ size_t text_line_index = 0;
// s_i .. shape index
for (size_t s_i = 0; s_i < shapes.size(); ++s_i) {
const ExPolygons &shape = shapes[s_i];
@@ -473,12 +481,36 @@ template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
if (!shape.empty()) {
bb = get_extents(shape);
}
- BoundingBoxes &line_bbs = bbs[text_line_index];
+ BoundingBoxes &line_bbs = result[text_line_index];
line_bbs.push_back(bb);
- if (ws[s_i] == '\n')
+ if (text[s_i] == '\n'){
+ // skip enters on beginig and tail
++text_line_index;
+ }
}
+ return result;
+}
+template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc was_canceled)
+{
+ // method use square of coord stored into int64_t
+ static_assert(std::is_same());
+
+ std::vector shapes = priv::create_shapes(input, was_canceled);
+ if (shapes.empty())
+ return {};
+
+ // Precalculate bounding boxes of glyphs
+ // Separate lines of text to vector of Bounds
+ const TextConfiguration &tc = input.text_configuration;
+ std::wstring ws = boost::nowide::widen(tc.text.c_str());
+ assert(get_count_lines(ws) == input.text_lines.size());
+ size_t count_lines = input.text_lines.size();
+ std::vector bbs = create_line_bounds(shapes, ws, count_lines);
+
+ const FontProp &prop = tc.style.prop;
+ FontFileWithCache &font = input.font_file;
+ double shape_scale = get_shape_scale(prop, *font.font_file);
double projec_scale = shape_scale / SHAPE_SCALE;
double depth = prop.emboss / projec_scale;
auto projectZ = std::make_unique(depth);
@@ -490,19 +522,18 @@ template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
size_t s_i_offset = 0; // shape index offset(for next lines)
indexed_triangle_set result;
- for (text_line_index = 0; text_line_index < input.text_lines.size(); ++text_line_index) {
+ for (size_t text_line_index = 0; text_line_index < input.text_lines.size(); ++text_line_index) {
const BoundingBoxes &line_bbs = bbs[text_line_index];
const TextLine &line = input.text_lines[text_line_index];
- // IMPROVE: do not precalculate samples do it inline - store result its
PolygonPoints samples = sample_slice(line, line_bbs, shape_scale);
std::vector angles = calculate_angles(em_2_polygon, samples, line.polygon);
for (size_t i = 0; i < line_bbs.size(); ++i) {
- const BoundingBox &line_bb = line_bbs[i];
- if (!line_bb.defined)
+ const BoundingBox &letter_bb = line_bbs[i];
+ if (!letter_bb.defined)
continue;
- Vec2d to_zero_vec = line_bb.center().cast() * shape_scale; // [in mm]
+ Vec2d to_zero_vec = letter_bb.center().cast() * shape_scale; // [in mm]
auto to_zero = Eigen::Translation(-to_zero_vec.x(), 0., 0.);
const double &angle = angles[i];
@@ -512,15 +543,16 @@ template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
Vec2d offset_vec = unscale(sample.point); // [in mm]
auto offset_tr = Eigen::Translation(offset_vec.x(), 0., -offset_vec.y());
- //Transform3d tr = offset_tr * rotate * to_zero * scale_tr;
Transform3d tr = offset_tr * rotate * to_zero * scale_tr;
- //Transform3d tr = Transform3d::Identity() * scale_tr;
const ExPolygons &letter_shape = shapes[s_i_offset + i];
-
+ assert(get_extents(letter_shape) == letter_bb);
auto projectZ = std::make_unique(depth);
ProjectTransform project(std::move(projectZ), tr);
indexed_triangle_set glyph_its = polygons2model(letter_shape, project);
its_merge(result, std::move(glyph_its));
+
+ if (((s_i_offset + i) % 15) && was_canceled())
+ return {};
}
s_i_offset += line_bbs.size();
@@ -549,10 +581,7 @@ template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
}
#endif // STORE_SAMPLING
}
-
- // ProjectTransform project(std::move(projectZ), )
- // if (was_canceled()) return {};
- return TriangleMesh(result);
+ return TriangleMesh(std::move(result));
}
} // namespace
@@ -864,22 +893,16 @@ OrthoProject3d priv::create_emboss_projection(
return OrthoProject3d(from_front_to_back);
}
-// input can't be const - cache of font
-TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2, std::function was_canceled)
-{
- ExPolygons shapes = create_shape(input1, was_canceled);
- if (shapes.empty())
- throw JobException(_u8L("Font doesn't have any shape for given text.").c_str());
-
- if (was_canceled()) return {};
+namespace {
+indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d& tr,const SurfaceVolumeData::ModelSources &sources, bool is_outside, DataBase& input, std::function was_canceled) {
+ assert(!sources.empty());
BoundingBox bb = get_extents(shapes);
- const FontFile &ff = *input1.font_file.font_file;
- const FontProp &fp = input1.text_configuration.style.prop;
+ const FontFile &ff = *input.font_file.font_file;
+ const FontProp &fp = input.text_configuration.style.prop;
double shape_scale = get_shape_scale(fp, ff);
- const SurfaceVolumeData::ModelSources &sources = input2.sources;
- const SurfaceVolumeData::ModelSource *biggest = nullptr;
+ const SurfaceVolumeData::ModelSource *biggest = &sources.front();
size_t biggest_count = 0;
// convert index from (s)ources to (i)ndexed (t)riangle (s)ets
@@ -888,9 +911,9 @@ TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2
itss.reserve(sources.size());
for (const SurfaceVolumeData::ModelSource &s : sources) {
Transform3d mesh_tr_inv = s.tr.inverse();
- Transform3d cut_projection_tr = mesh_tr_inv * input2.text_tr;
+ Transform3d cut_projection_tr = mesh_tr_inv * tr;
std::pair z_range{0., 1.};
- OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
+ OrthoProject cut_projection = priv::create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
// copy only part of source model
indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection);
if (its.indices.empty()) continue;
@@ -904,10 +927,10 @@ TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2
itss.emplace_back(std::move(its));
}
if (itss.empty())
- throw JobException(_u8L("There is no volume in projection direction.").c_str());
+ return {};
Transform3d tr_inv = biggest->tr.inverse();
- Transform3d cut_projection_tr = tr_inv * input2.text_tr;
+ Transform3d cut_projection_tr = tr_inv * tr;
size_t itss_index = s_to_itss[biggest - &sources.front()];
BoundingBoxf3 mesh_bb = bounding_box(itss[itss_index]);
@@ -929,22 +952,27 @@ TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2
Transform3d emboss_tr = cut_projection_tr.inverse();
BoundingBoxf3 mesh_bb_tr = mesh_bb.transformed(emboss_tr);
std::pair z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()};
- OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
- float projection_ratio = (-z_range.first + safe_extension) / (z_range.second - z_range.first + 2 * safe_extension);
+ OrthoProject cut_projection = priv::create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
+ float projection_ratio = (-z_range.first + priv::safe_extension) /
+ (z_range.second - z_range.first + 2 * priv::safe_extension);
- bool is_text_reflected = Slic3r::has_reflection(input2.text_tr);
+ ExPolygons shapes_data; // is used only when text is reflected to reverse polygon points order
+ const ExPolygons *shapes_ptr = &shapes;
+ bool is_text_reflected = Slic3r::has_reflection(tr);
if (is_text_reflected) {
// revert order of points in expolygons
// CW --> CCW
- for (ExPolygon &shape : shapes) {
+ shapes_data = shapes; // copy
+ for (ExPolygon &shape : shapes_data) {
shape.contour.reverse();
for (Slic3r::Polygon &hole : shape.holes)
hole.reverse();
}
+ shapes_ptr = &shapes_data;
}
// Use CGAL to cut surface from triangle mesh
- SurfaceCut cut = cut_surface(shapes, itss, cut_projection, projection_ratio);
+ SurfaceCut cut = cut_surface(*shapes_ptr, itss, cut_projection, projection_ratio);
if (is_text_reflected) {
for (SurfaceCut::Contour &c : cut.contours)
@@ -953,15 +981,105 @@ TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2
std::swap(t[0], t[1]);
}
- if (cut.empty()) throw JobException(_u8L("There is no valid surface for text projection.").c_str());
+ if (cut.empty()) return {}; // There is no valid surface for text projection.
if (was_canceled()) return {};
// !! Projection needs to transform cut
- OrthoProject3d projection = create_emboss_projection(input2.is_outside, fp.emboss, emboss_tr, cut);
- indexed_triangle_set new_its = cut2model(cut, projection);
- assert(!new_its.empty());
+ OrthoProject3d projection = priv::create_emboss_projection(is_outside, fp.emboss, emboss_tr, cut);
+ return cut2model(cut, projection);
+}
+
+TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &input2, std::function was_canceled)
+{
+ std::vector shapes = priv::create_shapes(input1, was_canceled);
if (was_canceled()) return {};
- return TriangleMesh(std::move(new_its));
+ if (shapes.empty())
+ throw priv::JobException(_u8L("Font doesn't have any shape for given text.").c_str());
+
+ // Precalculate bounding boxes of glyphs
+ // Separate lines of text to vector of Bounds
+ const TextConfiguration &tc = input1.text_configuration;
+ std::wstring ws = boost::nowide::widen(tc.text.c_str());
+ assert(get_count_lines(ws) == input1.text_lines.size());
+ size_t count_lines = input1.text_lines.size();
+ std::vector bbs = create_line_bounds(shapes, ws, count_lines);
+
+ const FontProp &prop = tc.style.prop;
+ FontFileWithCache &font = input1.font_file;
+ double shape_scale = get_shape_scale(prop, *font.font_file);
+
+ // half of font em size for direction of letter emboss
+ double em_2_mm = prop.size_in_mm / 2.;
+ int32_t em_2_polygon = static_cast(std::round(scale_(em_2_mm)));
+
+ size_t s_i_offset = 0; // shape index offset(for next lines)
+ indexed_triangle_set result;
+ for (size_t text_line_index = 0; text_line_index < input1.text_lines.size(); ++text_line_index) {
+ const BoundingBoxes &line_bbs = bbs[text_line_index];
+ const TextLine &line = input1.text_lines[text_line_index];
+ PolygonPoints samples = sample_slice(line, line_bbs, shape_scale);
+ std::vector angles = calculate_angles(em_2_polygon, samples, line.polygon);
+
+ for (size_t i = 0; i < line_bbs.size(); ++i) {
+ const BoundingBox &glyph_bb = line_bbs[i];
+ if (!glyph_bb.defined)
+ continue;
+
+ const double &angle = angles[i];
+ auto rotate = Eigen::AngleAxisd(angle + M_PI_2, Vec3d::UnitY());
+
+ const PolygonPoint &sample = samples[i];
+ Vec2d offset_vec = unscale(sample.point); // [in mm]
+ auto offset_tr = Eigen::Translation(offset_vec.x(), 0., -offset_vec.y());
+
+ ExPolygons &glyph_shape = shapes[s_i_offset + i];
+ assert(get_extents(glyph_shape) == glyph_bb);
+
+ Point offset(-glyph_bb.center().x(), 0);
+ for (ExPolygon& s: glyph_shape)
+ s.translate(offset);
+
+ Transform3d modify = offset_tr * rotate;
+ Transform3d tr = input2.text_tr * modify;
+ indexed_triangle_set glyph_its = cut_surface_to_its(glyph_shape, tr, input2.sources, input2.is_outside, input1, was_canceled);
+ // move letter in volume on the right position
+ its_transform(glyph_its, modify);
+
+ // Improve: union instead of merge
+ its_merge(result, std::move(glyph_its));
+
+ if (((s_i_offset + i) % 15) && was_canceled())
+ return {};
+ }
+ s_i_offset += line_bbs.size();
+ }
+
+ if (was_canceled()) return {};
+ if (result.empty())
+ throw priv::JobException(_u8L("There is no valid surface for text projection.").c_str());
+ return TriangleMesh(std::move(result));
+}
+
+} // namespace
+
+// input can't be const - cache of font
+TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2, std::function was_canceled)
+{
+ const FontProp &fp = input1.text_configuration.style.prop;
+ if (fp.per_glyph)
+ return cut_per_glyph_surface(input1, input2, was_canceled);
+
+ ExPolygons shapes = create_shape(input1, was_canceled);
+ if (was_canceled()) return {};
+ if (shapes.empty())
+ throw JobException(_u8L("Font doesn't have any shape for given text.").c_str());
+
+ indexed_triangle_set its = cut_surface_to_its(shapes, input2.text_tr, input2.sources, input2.is_outside, input1, was_canceled);
+ if (was_canceled()) return {};
+ if (its.empty())
+ throw JobException(_u8L("There is no valid surface for text projection.").c_str());
+
+ return TriangleMesh(std::move(its));
}
bool priv::process(std::exception_ptr &eptr) {
diff --git a/src/slic3r/GUI/TextLines.cpp b/src/slic3r/GUI/TextLines.cpp
index 50d6109c6b..a61133055e 100644
--- a/src/slic3r/GUI/TextLines.cpp
+++ b/src/slic3r/GUI/TextLines.cpp
@@ -317,7 +317,10 @@ void TextLinesModel::init(const Selection &selection, double line_height)
m_model.reset();
//*
- m_model.init_from(create_geometry(m_lines));
+ GLModel::Geometry geometry = create_geometry(m_lines);
+ if (geometry.vertices_count() == 0 || geometry.indices_count() == 0)
+ return;
+ m_model.init_from(std::move(geometry));
/*/
ColorRGBA color(.7f, .7f, .7f, .7f); // Transparent Gray
m_model.set_color(color);