mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-03 20:00:39 +08:00
Use surface when emboss per letter.
This commit is contained in:
parent
cf57bf02ff
commit
50f09a751b
@ -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;
|
||||
|
@ -425,9 +425,9 @@ namespace Emboss
|
||||
/// Sample slice polygon by bounding boxes centers
|
||||
/// slice start point has shape_center_x coor
|
||||
/// </summary>
|
||||
/// <param name="slice">Polygon and start point</param>
|
||||
/// <param name="bbs">Bounding boxes of letter on one line</param>
|
||||
/// <param name="scale">Scale for bbs</param>
|
||||
/// <param name="slice">Polygon and start point[Slic3r scaled milimeters]</param>
|
||||
/// <param name="bbs">Bounding boxes of letter on one line[in font scales]</param>
|
||||
/// <param name="scale">Scale for bbs (after multiply bb is in milimeters)</param>
|
||||
/// <returns>Sampled polygon by bounding boxes</returns>
|
||||
PolygonPoints sample_slice(const TextLine &slice, const BoundingBoxes &bbs, double scale);
|
||||
|
||||
|
@ -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<typename Fnc> static ExPolygons create_shape(DataBase &input, Fnc was_canceled);
|
||||
template<typename Fnc> static std::vector<ExPolygons> create_shapes(DataBase &input, Fnc was_canceled);
|
||||
|
||||
// <summary>
|
||||
/// 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<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc was_canceled)
|
||||
{
|
||||
// method use square of coord stored into int64_t
|
||||
static_assert(std::is_same<Point::coord_type, int32_t>());
|
||||
|
||||
template<typename Fnc>
|
||||
std::vector<ExPolygons> 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<ExPolygons> 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<BoundingBoxes> 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<BoundingBoxes> create_line_bounds(const std::vector<ExPolygons> &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<BoundingBoxes> 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<typename Fnc> 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<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc was_canceled)
|
||||
{
|
||||
// method use square of coord stored into int64_t
|
||||
static_assert(std::is_same<Point::coord_type, int32_t>());
|
||||
|
||||
std::vector<ExPolygons> 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<BoundingBoxes> 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<ProjectZ>(depth);
|
||||
@ -490,19 +522,18 @@ template<typename Fnc> 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<double> 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<double>() * shape_scale; // [in mm]
|
||||
Vec2d to_zero_vec = letter_bb.center().cast<double>() * shape_scale; // [in mm]
|
||||
auto to_zero = Eigen::Translation<double, 3>(-to_zero_vec.x(), 0., 0.);
|
||||
|
||||
const double &angle = angles[i];
|
||||
@ -512,15 +543,16 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
|
||||
Vec2d offset_vec = unscale(sample.point); // [in mm]
|
||||
auto offset_tr = Eigen::Translation<double, 3>(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<ProjectZ>(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<typename Fnc> 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<bool()> 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<bool()> 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<float, float> 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<float, float> z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()};
|
||||
OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
|
||||
float projection_ratio = (-z_range.first + safe_extension) / (z_range.second - z_range.first + 2 * safe_extension);
|
||||
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<bool()> was_canceled)
|
||||
{
|
||||
std::vector<ExPolygons> 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<BoundingBoxes> 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<int32_t>(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<double> 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<double, 3>(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<bool()> 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) {
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user