Run able fix after merge(still issue with scale for per glyph transformation)

This commit is contained in:
Filip Sykala - NTB T15p 2023-06-07 11:19:28 +02:00
parent e831255018
commit b71cb25163
10 changed files with 242 additions and 263 deletions

View File

@ -1270,20 +1270,27 @@ ExPolygons letter2shapes(
const int CANCEL_CHECK = 10; const int CANCEL_CHECK = 10;
} // namespace } // namespace
/// Union shape defined by glyphs
ExPolygons Slic3r::union_ex(const ExPolygonsWithIds &shapes)
{
// unify to one expolygon
ExPolygons result;
for (const ExPolygonsWithId &shape : shapes) {
if (shape.expoly.empty())
continue;
expolygons_append(result, shape.expoly);
}
result = union_ex(result);
heal_shape(result);
return result;
}
ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, const char *text, const FontProp &font_prop, const std::function<bool()>& was_canceled) ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, const char *text, const FontProp &font_prop, const std::function<bool()>& was_canceled)
{ {
std::wstring text_w = boost::nowide::widen(text); std::wstring text_w = boost::nowide::widen(text);
std::vector<ExPolygons> vshapes = text2vshapes(font_with_cache, text_w, font_prop, was_canceled); ExPolygonsWithIds vshapes = text2vshapes(font_with_cache, text_w, font_prop, was_canceled);
// unify to one expolygon return union_ex(vshapes);
ExPolygons result;
for (ExPolygons &shapes : vshapes) {
if (shapes.empty())
continue;
expolygons_append(result, std::move(shapes));
}
result = Slic3r::union_ex(result);
heal_shape(result);
return result;
} }
namespace { namespace {
@ -1295,10 +1302,10 @@ namespace {
/// Prerequisities: shapes are aligned left top</param> /// Prerequisities: shapes are aligned left top</param>
/// <param name="text">To detect end of lines</param> /// <param name="text">To detect end of lines</param>
/// <param name="line_height">Height of line for align[in font points]</param> /// <param name="line_height">Height of line for align[in font points]</param>
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shape, const std::wstring &text, int line_height); void align_shape(FontProp::Align type, ExPolygonsWithIds &shape, const std::wstring &text, int line_height);
} }
std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled){ ExPolygonsWithIds Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled){
assert(font_with_cache.has_value()); assert(font_with_cache.has_value());
const FontFile &font = *font_with_cache.font_file; const FontFile &font = *font_with_cache.font_file;
unsigned int font_index = font_prop.collection_number.value_or(0); unsigned int font_index = font_prop.collection_number.value_or(0);
@ -1309,7 +1316,7 @@ std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache,
Point cursor(0, 0); Point cursor(0, 0);
fontinfo_opt font_info_cache; fontinfo_opt font_info_cache;
std::vector<ExPolygons> result; ExPolygonsWithIds result;
result.reserve(text.size()); result.reserve(text.size());
for (wchar_t letter : text) { for (wchar_t letter : text) {
if (++counter == CANCEL_CHECK) { if (++counter == CANCEL_CHECK) {
@ -1317,7 +1324,8 @@ std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache,
if (was_canceled()) if (was_canceled())
return {}; return {};
} }
result.emplace_back(letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_cache)); unsigned id = static_cast<unsigned>(letter);
result.push_back({id, letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_cache)});
} }
align_shape(font_prop.align, result, text, get_line_height(font, font_prop)); align_shape(font_prop.align, result, text, get_line_height(font, font_prop));
@ -1358,6 +1366,16 @@ unsigned Emboss::get_count_lines(const std::string &text)
return get_count_lines(ws); return get_count_lines(ws);
} }
unsigned Emboss::get_count_lines(const ExPolygonsWithIds &shapes) {
if (shapes.empty())
return 0; // no glyphs
unsigned result = 1; // one line is minimum
for (const ExPolygonsWithId &shape_id : shapes)
if (shape_id.id == ENTER_UNICODE)
++result;
return result;
}
void Emboss::apply_transformation(const FontProp &font_prop, Transform3d &transformation){ void Emboss::apply_transformation(const FontProp &font_prop, Transform3d &transformation){
apply_transformation(font_prop.angle, font_prop.distance, transformation); apply_transformation(font_prop.angle, font_prop.distance, transformation);
} }
@ -1953,26 +1971,26 @@ int32_t get_align_x_offset(FontProp::HorizontalAlign align, const BoundingBox &s
return 0; return 0;
} }
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const std::wstring &text, int line_height) void align_shape(FontProp::Align type, ExPolygonsWithIds &shapes, const std::wstring &text, int line_height)
{ {
constexpr FontProp::Align no_change(FontProp::HorizontalAlign::left, FontProp::VerticalAlign::top); constexpr FontProp::Align no_change(FontProp::HorizontalAlign::left, FontProp::VerticalAlign::top);
if (type == no_change) if (type == no_change)
return; // no alignment return; // no alignment
BoundingBox shape_bb; BoundingBox shape_bb;
for (const ExPolygons& shape: shapes) for (const ExPolygonsWithId& shape: shapes)
shape_bb.merge(get_extents(shape)); shape_bb.merge(get_extents(shape.expoly));
auto get_line_bb = [&](size_t j) { auto get_line_bb = [&](size_t j) {
BoundingBox line_bb; BoundingBox line_bb;
for (; j < text.length() && text[j] != '\n'; ++j) for (; j < text.length() && text[j] != '\n'; ++j)
line_bb.merge(get_extents(shapes[j])); line_bb.merge(get_extents(shapes[j].expoly));
return line_bb; return line_bb;
}; };
Point offset( Point offset(
get_align_x_offset(type.first, shape_bb, get_line_bb(0)), get_align_x_offset(type.first, shape_bb, get_line_bb(0)),
get_align_y_offset(type.second, get_count_lines(text), line_height)); get_align_y_offset(type.second, get_count_lines(shapes), line_height));
assert(shapes.size() == text.length()); assert(shapes.size() == text.length());
for (size_t i = 0; i < shapes.size(); ++i) { for (size_t i = 0; i < shapes.size(); ++i) {
wchar_t letter = text[i]; wchar_t letter = text[i];
@ -1980,7 +1998,7 @@ void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const st
offset.x() = get_align_x_offset(type.first, shape_bb, get_line_bb(i+1)); offset.x() = get_align_x_offset(type.first, shape_bb, get_line_bb(i+1));
continue; continue;
} }
ExPolygons &shape = shapes[i]; ExPolygons &shape = shapes[i].expoly;
for (ExPolygon &s : shape) for (ExPolygon &s : shape)
s.translate(offset); s.translate(offset);
} }

View File

@ -8,6 +8,7 @@
#include <admesh/stl.h> // indexed_triangle_set #include <admesh/stl.h> // indexed_triangle_set
#include "Polygon.hpp" #include "Polygon.hpp"
#include "ExPolygon.hpp" #include "ExPolygon.hpp"
#include "EmbossShape.hpp" // ExPolygonsWithIds
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "TextConfiguration.hpp" #include "TextConfiguration.hpp"
@ -148,12 +149,14 @@ namespace Emboss
/// <param name="font_prop">User defined property of the font</param> /// <param name="font_prop">User defined property of the font</param>
/// <param name="was_canceled">Way to interupt processing</param> /// <param name="was_canceled">Way to interupt processing</param>
/// <returns>Inner polygon cw(outer ccw)</returns> /// <returns>Inner polygon cw(outer ccw)</returns>
ExPolygons text2shapes (FontFileWithCache &font, const char *text, const FontProp &font_prop, const std::function<bool()> &was_canceled = []() {return false;}); ExPolygons text2shapes (FontFileWithCache &font, const char *text, const FontProp &font_prop, const std::function<bool()> &was_canceled = []() {return false;});
std::vector<ExPolygons> text2vshapes(FontFileWithCache &font, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled = []() {return false;}); ExPolygonsWithIds text2vshapes(FontFileWithCache &font, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled = []() {return false;});
const unsigned ENTER_UNICODE = static_cast<unsigned>('\n');
/// Sum of character '\n' /// Sum of character '\n'
unsigned get_count_lines(const std::wstring &ws); unsigned get_count_lines(const std::wstring &ws);
unsigned get_count_lines(const std::string &text); unsigned get_count_lines(const std::string &text);
unsigned get_count_lines(const ExPolygonsWithIds &shape);
/// <summary> /// <summary>
/// Fix duplicit points and self intersections in polygons. /// Fix duplicit points and self intersections in polygons.
@ -448,5 +451,8 @@ namespace Emboss
std::vector<double> calculate_angles(int32_t distance, const PolygonPoints& polygon_points, const Polygon &polygon); std::vector<double> calculate_angles(int32_t distance, const PolygonPoints& polygon_points, const Polygon &polygon);
} // namespace Emboss } // namespace Emboss
ExPolygons union_ex(const ExPolygonsWithIds &shapes);
} // namespace Slic3r } // namespace Slic3r
#endif // slic3r_Emboss_hpp_ #endif // slic3r_Emboss_hpp_

View File

@ -5,6 +5,7 @@
#include <optional> #include <optional>
#include <cereal/cereal.hpp> #include <cereal/cereal.hpp>
#include <cereal/types/string.hpp> #include <cereal/types/string.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/types/optional.hpp> #include <cereal/types/optional.hpp>
#include <cereal/archives/binary.hpp> #include <cereal/archives/binary.hpp>
#include "Point.hpp" // Transform3d #include "Point.hpp" // Transform3d
@ -46,13 +47,28 @@ struct EmbossProjection
template<class Archive> void serialize(Archive &ar) { ar(depth, use_surface); } template<class Archive> void serialize(Archive &ar) { ar(depth, use_surface); }
}; };
// Help structure to identify expolygons grups
// e.g. emboss -> per glyph -> identify character
struct ExPolygonsWithId
{
// Identificator for shape
// In text it separate letters and the name is unicode value of letter
// Is svg it is id of path
unsigned id;
// shape defined by integer point contain only lines
// Curves are converted to sequence of lines
ExPolygons expoly;
};
using ExPolygonsWithIds = std::vector<ExPolygonsWithId>;
/// <summary> /// <summary>
/// Contain plane shape information to be able emboss it and edit it /// Contain plane shape information to be able emboss it and edit it
/// </summary> /// </summary>
struct EmbossShape struct EmbossShape
{ {
// shape defined by integer point consist only by lines not curves // shapes to to emboss separately over surface
ExPolygons shapes; ExPolygonsWithIds shapes_with_ids;
// scale of shape, multiplier to get 3d point in mm from integer shape // scale of shape, multiplier to get 3d point in mm from integer shape
double scale = 1.; double scale = 1.;
@ -74,16 +90,21 @@ struct EmbossShape
// undo / redo stack recovery // undo / redo stack recovery
template<class Archive> void save(Archive &ar) const template<class Archive> void save(Archive &ar) const
{ {
ar(shapes, scale, projection, svg_file_path); ar(shapes_with_ids, scale, projection, svg_file_path);
cereal::save(ar, fix_3mf_tr); cereal::save(ar, fix_3mf_tr);
} }
template<class Archive> void load(Archive &ar) template<class Archive> void load(Archive &ar)
{ {
ar(shapes, scale, projection, svg_file_path); ar(shapes_with_ids, scale, projection, svg_file_path);
cereal::load(ar, fix_3mf_tr); cereal::load(ar, fix_3mf_tr);
} }
}; };
} // namespace Slic3r } // namespace Slic3r
// Serialization through the Cereal library
namespace cereal {
template<class Archive> void serialize(Archive &ar, Slic3r::ExPolygonsWithId &o) { ar(o.id, o.expoly); }
}; // namespace cereal
#endif // slic3r_EmbossShape_hpp_ #endif // slic3r_EmbossShape_hpp_

View File

@ -3681,7 +3681,9 @@ Transform3d create_fix(const std::optional<Transform3d> &prev, const ModelVolume
return *prev * fix_trmat; return *prev * fix_trmat;
} }
std::string to_string(const ExPolygons& expolygons){ std::string to_string(const ExPolygonsWithIds &shapes)
{
// TODO: Need to implement
return {}; return {};
} }
@ -3692,7 +3694,7 @@ void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume
stream << SVG_FILE_PATH_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(es.svg_file_path) << "\" "; stream << SVG_FILE_PATH_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(es.svg_file_path) << "\" ";
stream << SHAPE_SCALE_ATTR << "=\"" << es.scale << "\" "; stream << SHAPE_SCALE_ATTR << "=\"" << es.scale << "\" ";
std::string expolygons_str = to_string(es.shapes); // cereal serialize expolygons std::string expolygons_str = to_string(es.shapes_with_ids); // cereal serialize expolygons
stream << SHAPE_EXPOLYS_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(expolygons_str) << "\" "; stream << SHAPE_EXPOLYS_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(expolygons_str) << "\" ";
// projection // projection
@ -3710,7 +3712,7 @@ void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume
} }
std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes) { std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes) {
ExPolygons shapes;
double scale = get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR); double scale = get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR);
EmbossProjection projection; EmbossProjection projection;
@ -3725,7 +3727,8 @@ std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned i
fix_tr_mat = get_transform_from_3mf_specs_string(fix_tr_mat_str); fix_tr_mat = get_transform_from_3mf_specs_string(fix_tr_mat_str);
} }
std::string file_path = get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_ATTR); std::string file_path = get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_ATTR);
return EmbossShape{std::move(shapes), scale, std::move(projection), std::move(fix_tr_mat), std::move(file_path)}; ExPolygonsWithIds shapes; // TODO: need to implement
return EmbossShape{shapes, scale, std::move(projection), std::move(fix_tr_mat), std::move(file_path)};
} }

View File

@ -345,7 +345,7 @@ bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
return false; return false;
// NOTE: change style manager - be carefull with order changes // NOTE: change style manager - be carefull with order changes
DataBasePtr base = create_emboss_data_base(m_text, m_style_manager, m_job_cancel); DataBasePtr base = create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), volume_type, m_job_cancel);
CreateVolumeParams input = create_input(m_parent, m_style_manager.get_style(), m_raycast_manager, volume_type); CreateVolumeParams input = create_input(m_parent, m_style_manager.get_style(), m_raycast_manager, volume_type);
return start_create_volume(input, std::move(base), mouse_pos); return start_create_volume(input, std::move(base), mouse_pos);
} }
@ -357,7 +357,7 @@ bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
return false; return false;
// NOTE: change style manager - be carefull with order changes // NOTE: change style manager - be carefull with order changes
DataBasePtr base = create_emboss_data_base(m_text, m_style_manager, m_job_cancel); DataBasePtr base = create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), volume_type, m_job_cancel);
CreateVolumeParams input = create_input(m_parent, m_style_manager.get_style(), m_raycast_manager, volume_type); CreateVolumeParams input = create_input(m_parent, m_style_manager.get_style(), m_raycast_manager, volume_type);
return start_create_volume_without_position(input, std::move(base)); return start_create_volume_without_position(input, std::move(base));
} }
@ -1043,9 +1043,6 @@ std::optional<wxString> get_installed_face_name(const std::optional<std::string>
} }
return {}; // not installed return {}; // not installed
} }
} // namespace
namespace {
bool get_line_height_offset(/* const*/ StyleManager &style_manager, double &line_height_mm, double &line_offset_mm) bool get_line_height_offset(/* const*/ StyleManager &style_manager, double &line_height_mm, double &line_offset_mm)
{ {
@ -1066,8 +1063,8 @@ bool get_line_height_offset(/* const*/ StyleManager &style_manager, double &line
double third_ascent_shape_size = ff.infos[fp.collection_number.value_or(0)].ascent / 3.; double third_ascent_shape_size = ff.infos[fp.collection_number.value_or(0)].ascent / 3.;
int line_height_shape_size = get_line_height(ff, fp); // In shape size int line_height_shape_size = get_line_height(ff, fp); // In shape size
double scale = get_shape_scale(fp, ff); double scale = get_text_shape_scale(fp, ff);
line_offset_mm = third_ascent_shape_size * scale / SHAPE_SCALE; line_offset_mm = third_ascent_shape_size * scale / 0.001; // TODO:fix constatnt SHAPE_SCALE
line_height_mm = line_height_shape_size * scale; line_height_mm = line_height_shape_size * scale;
if (line_height_mm < 0) if (line_height_mm < 0)
@ -1279,7 +1276,7 @@ void GLGizmoEmboss::set_volume_by_selection()
// Calculate current angle of up vector // Calculate current angle of up vector
assert(m_style_manager.is_active_font()); assert(m_style_manager.is_active_font());
if (m_style_manager.is_active_font()) if (m_style_manager.is_active_font())
m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit); m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), up_limit);
// calculate scale for height and depth inside of scaled object instance // calculate scale for height and depth inside of scaled object instance
calculate_scale(); calculate_scale();
@ -1322,6 +1319,10 @@ void GLGizmoEmboss::calculate_scale() {
m_style_manager.clear_imgui_font(); m_style_manager.clear_imgui_font();
} }
namespace {
bool is_text_empty(std::string_view text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; }
} // namespace
bool GLGizmoEmboss::process() bool GLGizmoEmboss::process()
{ {
// no volume is selected -> selection from right panel // no volume is selected -> selection from right panel
@ -1329,27 +1330,26 @@ bool GLGizmoEmboss::process()
if (m_volume == nullptr) return false; if (m_volume == nullptr) return false;
// without text there is nothing to emboss // without text there is nothing to emboss
if (priv::is_text_empty(m_text)) return false; if (is_text_empty(m_text)) return false;
// exist loaded font file? // exist loaded font file?
if (!m_style_manager.is_active_font()) return false; if (!m_style_manager.is_active_font()) return false;
DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_volume->type(), m_job_cancel), DataUpdate data{create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_volume->type(), m_job_cancel),
m_volume->id()}; m_volume->id()};
std::unique_ptr<Job> job = nullptr; std::unique_ptr<Job> job = nullptr;
// check cutting from source mesh // check cutting from source mesh
bool &use_surface = data.text_configuration.style.prop.use_surface; bool &use_surface = data.base->shape.projection.use_surface;
bool is_object = m_volume->get_object()->volumes.size() == 1; if (use_surface && m_volume->is_the_only_one_part())
if (use_surface && is_object)
use_surface = false; use_surface = false;
assert(!data.text_configuration.style.prop.per_glyph || assert(data.base->text_lines.empty() ||
get_count_lines(m_text) == m_text_lines.get_lines().size()); data.base->text_lines.size() == get_count_lines(m_text));
if (use_surface) { if (use_surface) {
// Model to cut surface from. // Model to cut surface from.
SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume); SurfaceVolumeData::ModelSources sources = create_volume_sources(*m_volume);
if (sources.empty()) if (sources.empty())
return false; return false;
@ -1382,7 +1382,7 @@ bool GLGizmoEmboss::process()
queue_job(worker, std::move(job)); queue_job(worker, std::move(job));
#else #else
// Run Job on main thread (blocking) - ONLY DEBUG // Run Job on main thread (blocking) - ONLY DEBUG
priv::execute_job(std::move(job)); execute_job(std::move(job));
#endif // EXECUTE_PROCESS_ON_MAIN_THREAD #endif // EXECUTE_PROCESS_ON_MAIN_THREAD
// notification is removed befor object is changed by job // notification is removed befor object is changed by job
@ -1390,10 +1390,6 @@ bool GLGizmoEmboss::process()
return true; return true;
} }
namespace {
bool is_text_empty(std::string_view text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; }
}
void GLGizmoEmboss::close() void GLGizmoEmboss::close()
{ {
// remove volume when text is empty // remove volume when text is empty
@ -1506,7 +1502,7 @@ void GLGizmoEmboss::draw_window()
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Checkbox("##ALLOW_OPEN_NEAR_VOLUME", &m_allow_open_near_volume)) { if (ImGui::Checkbox("##ALLOW_OPEN_NEAR_VOLUME", &m_allow_open_near_volume)) {
if (m_allow_open_near_volume) if (m_allow_open_near_volume)
m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size()); m_set_window_offset = calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size());
} else if (ImGui::IsItemHovered()) { } else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", ((m_allow_open_near_volume) ? ImGui::SetTooltip("%s", ((m_allow_open_near_volume) ?
"Fix settings position": "Fix settings position":
@ -2598,17 +2594,6 @@ void GLGizmoEmboss::draw_height(bool use_inch)
process(); process();
} }
bool GLGizmoEmboss::set_depth()
{
float &value = m_style_manager.get_font_prop().emboss;
// size can't be zero or negative
priv::Limits::apply(value, priv::limits.emboss);
// only different value need process
return !is_approx(value, m_volume->text_configuration->style.prop.emboss);
}
void GLGizmoEmboss::draw_depth(bool use_inch) void GLGizmoEmboss::draw_depth(bool use_inch)
{ {
double &value = m_style_manager.get_style().projection.depth; double &value = m_style_manager.get_style().projection.depth;
@ -2745,6 +2730,7 @@ void GLGizmoEmboss::draw_advanced()
} }
m_imgui->disabled_end(); // !can_use_surface m_imgui->disabled_end(); // !can_use_surface
FontProp& font_prop = m_style_manager.get_font_prop();
bool &per_glyph = font_prop.per_glyph; bool &per_glyph = font_prop.per_glyph;
bool can_use_per_glyph = (per_glyph) ? true : // already used surface must have option to uncheck bool can_use_per_glyph = (per_glyph) ? true : // already used surface must have option to uncheck
!is_the_only_one_part; !is_the_only_one_part;
@ -2777,9 +2763,9 @@ void GLGizmoEmboss::draw_advanced()
ImGui::SetTooltip("TEST PURPOSE ONLY\nMove base line (up/down) for allign letters"); ImGui::SetTooltip("TEST PURPOSE ONLY\nMove base line (up/down) for allign letters");
m_imgui->disabled_end(); // !per_glyph m_imgui->disabled_end(); // !per_glyph
auto draw_align = [&align = font_prop.align, gui_cfg = m_gui_cfg, &icons = m_icons]() { auto draw_align = [&align = font_prop.align, input_offset = m_gui_cfg->advanced_input_offset, &icons = m_icons]() {
bool is_change = false; bool is_change = false;
ImGui::SameLine(gui_cfg->advanced_input_offset); ImGui::SameLine(input_offset);
if (align.first==FontProp::HorizontalAlign::left) draw(get_icon(icons, IconType::align_horizontal_left, IconState::hovered)); if (align.first==FontProp::HorizontalAlign::left) draw(get_icon(icons, IconType::align_horizontal_left, IconState::hovered));
else if (draw_button(icons, IconType::align_horizontal_left)) { align.first=FontProp::HorizontalAlign::left; is_change = true; } else if (draw_button(icons, IconType::align_horizontal_left)) { align.first=FontProp::HorizontalAlign::left; is_change = true; }
else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Set left alignment").c_str()); else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Set left alignment").c_str());
@ -3013,7 +2999,7 @@ void GLGizmoEmboss::draw_advanced()
assert(get_selected_volume(m_parent.get_selection()) == m_volume); assert(get_selected_volume(m_parent.get_selection()) == m_volume);
const Camera &cam = wxGetApp().plater()->get_camera(); const Camera &cam = wxGetApp().plater()->get_camera();
if (face_selected_volume_to_camera(cam, m_parent) && if (face_selected_volume_to_camera(cam, m_parent) &&
(use_surface || prop.per_glyph)) (use_surface || m_style_manager.get_font_prop().per_glyph))
process(); process();
} else if (ImGui::IsItemHovered()) { } else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", _u8L("Orient the text towards the camera.").c_str()); ImGui::SetTooltip("%s", _u8L("Orient the text towards the camera.").c_str());
@ -3149,7 +3135,7 @@ bool GLGizmoEmboss::choose_true_type_file()
// use first valid font // use first valid font
for (auto &input_file : input_files) { for (auto &input_file : input_files) {
std::string path = std::string(input_file.c_str()); std::string path = std::string(input_file.c_str());
std::string name = priv::get_file_name(path); std::string name = get_file_name(path);
//make_unique_name(name, m_font_list); //make_unique_name(name, m_font_list);
const FontProp& prop = m_style_manager.get_font_prop(); const FontProp& prop = m_style_manager.get_font_prop();
EmbossStyle style{ name, path, EmbossStyle::Type::file_path, prop }; EmbossStyle style{ name, path, EmbossStyle::Type::file_path, prop };
@ -3306,14 +3292,16 @@ TextDataBase::TextDataBase(DataBase &&parent,
EmbossShape &TextDataBase::create_shape() EmbossShape &TextDataBase::create_shape()
{ {
if (!shape.shapes.empty()) if (!shape.shapes_with_ids.empty())
return shape; return shape;
// create shape by configuration // create shape by configuration
const char *text = m_text_configuration.text.c_str(); const char *text = m_text_configuration.text.c_str();
std::wstring text_w = boost::nowide::widen(text);
const FontProp &fp = m_text_configuration.style.prop; const FontProp &fp = m_text_configuration.style.prop;
auto was_canceled = [&c = cancel](){ return c->load(); }; auto was_canceled = [&c = cancel](){ return c->load(); };
shape.shapes = text2shapes(m_font_file, text, fp, was_canceled);
shape.shapes_with_ids = text2vshapes(m_font_file, text_w, fp, was_canceled);
return shape; return shape;
} }
@ -3370,7 +3358,7 @@ std::unique_ptr<DataBase> create_emboss_data_base(const std::string
assert(style_manager.get_wx_font().IsOk()); assert(style_manager.get_wx_font().IsOk());
assert(style.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0); assert(style.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0);
if (es.prop.per_glyph) { if (style.prop.per_glyph) {
if (!text_lines.is_init()) if (!text_lines.is_init())
init_text_lines(text_lines, selection, style_manager); init_text_lines(text_lines, selection, style_manager);
} else } else
@ -3386,9 +3374,12 @@ std::unique_ptr<DataBase> create_emboss_data_base(const std::string
// create new shared ptr to cancel new job // create new shared ptr to cancel new job
cancel = std::make_shared<std::atomic<bool>>(false); cancel = std::make_shared<std::atomic<bool>>(false);
DataBase base(volume_name, cancel); DataBase base(volume_name, cancel);
base.is_outside = is_outside;
base.text_lines = text_lines.get_lines();
FontFileWithCache &font = style_manager.get_font_file_with_cache(); FontFileWithCache &font = style_manager.get_font_file_with_cache();
TextConfiguration tc{static_cast<EmbossStyle>(style), text}; TextConfiguration tc{static_cast<EmbossStyle>(style), text};
return std::make_unique<TextDataBase>(std::move(base), font, std::move(tc), style.projection, is_outside, text_lines.get_lines()); return std::make_unique<TextDataBase>(std::move(base), font, std::move(tc), style.projection);
} }
CreateVolumeParams create_input(GLCanvas3D &canvas, const StyleManager::Style &style, RaycastManager& raycaster, ModelVolumeType volume_type) CreateVolumeParams create_input(GLCanvas3D &canvas, const StyleManager::Style &style, RaycastManager& raycaster, ModelVolumeType volume_type)
@ -3696,6 +3687,8 @@ GuiCfg create_gui_configuration()
cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space; cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space;
tr.use_surface = _u8L("Use surface"); tr.use_surface = _u8L("Use surface");
tr.per_glyph = _u8L("Per glyph orientation");
tr.alignment = _u8L("Alignment");
tr.char_gap = _u8L("Char gap"); tr.char_gap = _u8L("Char gap");
tr.line_gap = _u8L("Line gap"); tr.line_gap = _u8L("Line gap");
tr.boldness = _u8L("Boldness"); tr.boldness = _u8L("Boldness");
@ -3707,6 +3700,8 @@ GuiCfg create_gui_configuration()
float max_advanced_text_width = std::max({ float max_advanced_text_width = std::max({
ImGui::CalcTextSize(tr.use_surface.c_str()).x, ImGui::CalcTextSize(tr.use_surface.c_str()).x,
ImGui::CalcTextSize(tr.per_glyph.c_str()).x,
ImGui::CalcTextSize(tr.alignment.c_str()).x,
ImGui::CalcTextSize(tr.char_gap.c_str()).x, ImGui::CalcTextSize(tr.char_gap.c_str()).x,
ImGui::CalcTextSize(tr.line_gap.c_str()).x, ImGui::CalcTextSize(tr.line_gap.c_str()).x,
ImGui::CalcTextSize(tr.boldness.c_str()).x, ImGui::CalcTextSize(tr.boldness.c_str()).x,
@ -3742,7 +3737,7 @@ GuiCfg create_gui_configuration()
// 9 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, keepUp, textFaceToCamera // 9 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, keepUp, textFaceToCamera
// 4 = 1px for fix each edit image of drag float // 4 = 1px for fix each edit image of drag float
float advance_height = input_height * 8 + 8; float advance_height = input_height * 10 + 9;
cfg.minimal_window_size_with_advance = cfg.minimal_window_size_with_advance =
ImVec2(cfg.minimal_window_size.x, ImVec2(cfg.minimal_window_size.x,
cfg.minimal_window_size.y + advance_height); cfg.minimal_window_size.y + advance_height);

View File

@ -483,7 +483,7 @@ void GLGizmoSVG::reset_volume()
m_volume = nullptr; m_volume = nullptr;
m_volume_id.id = 0; m_volume_id.id = 0;
m_volume_shape.shapes.clear(); m_volume_shape.shapes_with_ids.clear();
} }
void GLGizmoSVG::calculate_scale() { void GLGizmoSVG::calculate_scale() {
@ -562,7 +562,7 @@ void GLGizmoSVG::draw_window()
ImGui::Unindent(m_gui_cfg->icon_width); ImGui::Unindent(m_gui_cfg->icon_width);
if (ImGui::Button("change file")) { if (ImGui::Button("change file")) {
m_volume_shape.shapes = select_shape().shapes; m_volume_shape.shapes_with_ids = select_shape().shapes_with_ids;
// TODO: use setted scale // TODO: use setted scale
process(); process();
} }
@ -597,6 +597,16 @@ void GLGizmoSVG::draw_depth()
ImGui::SetTooltip("%s", _u8L("Size in emboss direction.").c_str()); ImGui::SetTooltip("%s", _u8L("Size in emboss direction.").c_str());
} }
namespace Slic3r {
BoundingBox get_extents(const ExPolygonsWithIds &expoly_ids)
{
BoundingBox result;
for (const ExPolygonsWithId &expoly_id : expoly_ids)
result.merge(get_extents(expoly_id.expoly));
return result;
}
} // namespace Slic3r
void GLGizmoSVG::draw_size() void GLGizmoSVG::draw_size()
{ {
ImGuiWrapper::text(m_gui_cfg->translations.size); ImGuiWrapper::text(m_gui_cfg->translations.size);
@ -610,7 +620,7 @@ void GLGizmoSVG::draw_size()
ImGui::SetNextItemWidth(input_width); ImGui::SetNextItemWidth(input_width);
// TODO: cache it // TODO: cache it
BoundingBox bb = get_extents(m_volume_shape.shapes); BoundingBox bb = get_extents(m_volume_shape.shapes_with_ids);
Point size = bb.size(); Point size = bb.size();
const char *size_format = (use_inch) ? "%.2f in" : "%.1f mm"; const char *size_format = (use_inch) ? "%.2f in" : "%.1f mm";
@ -992,6 +1002,7 @@ ExPolygons default_shape()
std::string file = Slic3r::resources_dir() + "/icons/question.svg"; std::string file = Slic3r::resources_dir() + "/icons/question.svg";
NSVGimage *image = nsvgParseFromFile(file.c_str(), "px", 96.0f); NSVGimage *image = nsvgParseFromFile(file.c_str(), "px", 96.0f);
ExPolygons shape = to_expolygons(image); ExPolygons shape = to_expolygons(image);
assert(!shape.empty());
nsvgDelete(image); nsvgDelete(image);
return shape; return shape;
} }
@ -1018,11 +1029,14 @@ EmbossShape select_shape()
int max_level = 10; int max_level = 10;
float scale = static_cast<float>(1 / shape.scale); float scale = static_cast<float>(1 / shape.scale);
bool is_y_negative = true; bool is_y_negative = true;
shape.shapes = to_expolygons(image, tesselation_tolerance, max_level, scale, is_y_negative); ExPolygons expoly = to_expolygons(image, tesselation_tolerance, max_level, scale, is_y_negative);
// Must contain some shapes !!! // Must contain some shapes !!!
if (shape.shapes.empty()) if (expoly.empty())
shape.shapes = default_shape(); expoly = default_shape();
unsigned id = 0;
shape.shapes_with_ids = {{id, expoly}};
return shape; return shape;
} }
@ -1031,7 +1045,7 @@ DataBasePtr create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel)
{ {
EmbossShape shape = select_shape(); EmbossShape shape = select_shape();
if (shape.shapes.empty()) if (shape.shapes_with_ids.empty())
// canceled selection of SVG file // canceled selection of SVG file
return nullptr; return nullptr;

View File

@ -160,7 +160,6 @@ bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
bool check(const UpdateSurfaceVolumeData &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 ExPolygons create_shape(DataBase &input, Fnc was_canceled);
template<typename Fnc> static std::vector<ExPolygons> create_shapes(DataBase &input, Fnc was_canceled);
// create sure that emboss object is bigger than source object [in mm] // create sure that emboss object is bigger than source object [in mm]
constexpr float safe_extension = 1.0f; constexpr float safe_extension = 1.0f;
@ -273,10 +272,9 @@ void Slic3r::GUI::Emboss::DataBase::write(ModelVolume &volume) const{
CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input): m_input(std::move(input)){ assert(check(m_input, true)); } CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input): m_input(std::move(input)){ assert(check(m_input, true)); }
void CreateVolumeJob::process(Ctl &ctl) { void CreateVolumeJob::process(Ctl &ctl) {
if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossCreateVolumeJob."); if (!check(m_input))
auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); }; throw std::runtime_error("Bad input data for EmbossCreateVolumeJob.");
m_result = priv::create_mesh(m_input, was_canceled, ctl); m_result = create_mesh(*m_input.base, was_canceled(ctl, *m_input.base), ctl);
//m_result = create_mesh(*m_input.base, was_canceled(ctl, *m_input.base), ctl); // svg
} }
void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
if (!::finalize(canceled, eptr, *m_input.base)) if (!::finalize(canceled, eptr, *m_input.base))
@ -393,7 +391,7 @@ void UpdateJob::process(Ctl &ctl)
m_result = ::try_create_mesh(*m_input.base, was_canceled); m_result = ::try_create_mesh(*m_input.base, was_canceled);
if (was_canceled()) return; if (was_canceled()) return;
if (m_result.its.empty()) if (m_result.its.empty())
throw priv::JobException("Created text volume is empty. Change text or font."); throw JobException("Created text volume is empty. Change text or font.");
} }
void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr) void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr)
@ -658,10 +656,7 @@ bool start_update_volume(DataUpdate &&data, const ModelVolume &volume, const Sel
volume_tr *= Eigen::Translation<double, 3>(*offset); volume_tr *= Eigen::Translation<double, 3>(*offset);
} }
bool is_outside = volume.is_model_part(); UpdateSurfaceVolumeData surface_data{std::move(data), {volume_tr, std::move(sources)}};
// check that there is not unexpected volume type
assert(is_outside || volume.is_negative_volume() || volume.is_modifier());
UpdateSurfaceVolumeData surface_data{std::move(data), {volume_tr, is_outside, std::move(sources)}};
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data)); job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
} else { } else {
job = std::make_unique<UpdateJob>(std::move(data)); job = std::make_unique<UpdateJob>(std::move(data));
@ -694,13 +689,13 @@ bool check(const DataBase &input, bool check_fontfile, bool use_surface)
// res &= !input.text_configuration.text.empty(); // res &= !input.text_configuration.text.empty();
assert(!input.volume_name.empty()); assert(!input.volume_name.empty());
res &= !input.volume_name.empty(); res &= !input.volume_name.empty();
const FontProp& prop = input.text_configuration.style.prop; //const FontProp& prop = input.text_configuration.style.prop;
assert(prop.per_glyph == !input.text_lines.empty()); //assert(prop.per_glyph == !input.text_lines.empty());
res &= prop.per_glyph == !input.text_lines.empty(); //res &= prop.per_glyph == !input.text_lines.empty();
if (prop.per_glyph) { //if (prop.per_glyph) {
assert(get_count_lines(input.text_configuration.text) == input.text_lines.size()); // assert(get_count_lines(input.text_configuration.text) == input.text_lines.size());
res &= get_count_lines(input.text_configuration.text) == input.text_lines.size(); // res &= get_count_lines(input.text_configuration.text) == input.text_lines.size();
} //}
return res; return res;
} }
@ -791,70 +786,34 @@ bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread)
} }
template<typename Fnc> template<typename Fnc>
ExPolygons priv::create_shape(DataBase &input, Fnc was_canceled) { ExPolygons create_shape(DataBase &input, Fnc was_canceled) {
FontFileWithCache &font = input.font_file; const EmbossShape &es = input.create_shape();
const TextConfiguration &tc = input.text_configuration; return union_ex(es.shapes_with_ids);
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 {};
ExPolygons shapes = text2shapes(font, text, prop, was_canceled);
if (shapes.empty())
return {};
return shapes;
}
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;
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 {};
if (was_canceled())
return {};
return shapes;
} }
//#define STORE_SAMPLING //#define STORE_SAMPLING
#ifdef STORE_SAMPLING #ifdef STORE_SAMPLING
#include "libslic3r/SVG.hpp" #include "libslic3r/SVG.hpp"
#endif // STORE_SAMPLING #endif // STORE_SAMPLING
namespace {
std::vector<BoundingBoxes> create_line_bounds(const std::vector<ExPolygons> &shapes, const std::wstring& text, size_t count_lines = 0) std::vector<BoundingBoxes> create_line_bounds(const ExPolygonsWithIds &shapes, size_t count_lines = 0)
{ {
assert(text.size() == shapes.size());
if (count_lines == 0) if (count_lines == 0)
count_lines = get_count_lines(text); count_lines = get_count_lines(shapes);
assert(count_lines == get_count_lines(text)); assert(count_lines == get_count_lines(shapes));
std::vector<BoundingBoxes> result(count_lines); std::vector<BoundingBoxes> result(count_lines);
size_t text_line_index = 0; size_t text_line_index = 0;
// s_i .. shape index // s_i .. shape index
for (size_t s_i = 0; s_i < shapes.size(); ++s_i) { for (const ExPolygonsWithId &shape_id: shapes) {
const ExPolygons &shape = shapes[s_i]; const ExPolygons &shape = shape_id.expoly;
BoundingBox bb; BoundingBox bb;
if (!shape.empty()) { if (!shape.empty()) {
bb = get_extents(shape); bb = get_extents(shape);
} }
BoundingBoxes &line_bbs = result[text_line_index]; BoundingBoxes &line_bbs = result[text_line_index];
line_bbs.push_back(bb); line_bbs.push_back(bb);
if (text[s_i] == '\n'){ if (shape_id.id == ENTER_UNICODE) {
// skip enters on beginig and tail // skip enters on beginig and tail
++text_line_index; ++text_line_index;
} }
@ -866,28 +825,23 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
{ {
// method use square of coord stored into int64_t // method use square of coord stored into int64_t
static_assert(std::is_same<Point::coord_type, int32_t>()); static_assert(std::is_same<Point::coord_type, int32_t>());
const EmbossShape &shape = input.create_shape();
std::vector<ExPolygons> shapes = priv::create_shapes(input, was_canceled); if (shape.shapes_with_ids.empty())
if (shapes.empty())
return {}; return {};
// Precalculate bounding boxes of glyphs // Precalculate bounding boxes of glyphs
// Separate lines of text to vector of Bounds // Separate lines of text to vector of Bounds
const TextConfiguration &tc = input.text_configuration; assert(get_count_lines(shape.shapes_with_ids) == input.text_lines.size());
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(); size_t count_lines = input.text_lines.size();
std::vector<BoundingBoxes> bbs = create_line_bounds(shapes, ws, count_lines); std::vector<BoundingBoxes> bbs = create_line_bounds(shape.shapes_with_ids, count_lines);
const FontProp &prop = tc.style.prop; double projec_scale = shape.scale / 0.001; // SHAPE_SCALE;
FontFileWithCache &font = input.font_file; double depth = shape.projection.depth / projec_scale;
double shape_scale = get_shape_scale(prop, *font.font_file);
double projec_scale = shape_scale / SHAPE_SCALE;
double depth = prop.emboss / projec_scale;
auto scale_tr = Eigen::Scaling(projec_scale); auto scale_tr = Eigen::Scaling(projec_scale);
// half of font em size for direction of letter emboss // half of font em size for direction of letter emboss
double em_2_mm = prop.size_in_mm / 2.; // double em_2_mm = prop.size_in_mm / 2.; // TODO: fix it
double em_2_mm = 5.;
int32_t em_2_polygon = static_cast<int32_t>(std::round(scale_(em_2_mm))); 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) size_t s_i_offset = 0; // shape index offset(for next lines)
@ -895,7 +849,7 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
for (size_t 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 BoundingBoxes &line_bbs = bbs[text_line_index];
const TextLine &line = input.text_lines[text_line_index]; const TextLine &line = input.text_lines[text_line_index];
PolygonPoints samples = sample_slice(line, line_bbs, shape_scale); PolygonPoints samples = sample_slice(line, line_bbs, shape.scale);
std::vector<double> angles = calculate_angles(em_2_polygon, samples, line.polygon); std::vector<double> angles = calculate_angles(em_2_polygon, samples, line.polygon);
for (size_t i = 0; i < line_bbs.size(); ++i) { for (size_t i = 0; i < line_bbs.size(); ++i) {
@ -903,10 +857,12 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
if (!letter_bb.defined) if (!letter_bb.defined)
continue; continue;
Vec2d to_zero_vec = letter_bb.center().cast<double>() * shape_scale; // [in mm] Vec2d to_zero_vec = letter_bb.center().cast<double>() * shape.scale; // [in mm]
float surface_offset = input.is_outside ? -priv::SAFE_SURFACE_OFFSET : (-prop.emboss + priv::SAFE_SURFACE_OFFSET); float surface_offset = input.is_outside ? -SAFE_SURFACE_OFFSET : (-shape.projection.depth + SAFE_SURFACE_OFFSET);
if (prop.distance.has_value())
surface_offset += *prop.distance; // TODO: fix it
//if (prop.distance.has_value())
// surface_offset += *prop.distance;
Eigen::Translation<double, 3> to_zero(-to_zero_vec.x(), 0., static_cast<double>(surface_offset)); Eigen::Translation<double, 3> to_zero(-to_zero_vec.x(), 0., static_cast<double>(surface_offset));
@ -918,7 +874,7 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
Eigen::Translation<double, 3> offset_tr(offset_vec.x(), 0., -offset_vec.y()); Eigen::Translation<double, 3> offset_tr(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;
const ExPolygons &letter_shape = shapes[s_i_offset + i]; const ExPolygons &letter_shape = shape.shapes_with_ids[s_i_offset + i].expoly;
assert(get_extents(letter_shape) == letter_bb); assert(get_extents(letter_shape) == letter_bb);
auto projectZ = std::make_unique<ProjectZ>(depth); auto projectZ = std::make_unique<ProjectZ>(depth);
ProjectTransform project(std::move(projectZ), tr); ProjectTransform project(std::move(projectZ), tr);
@ -958,24 +914,8 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
return TriangleMesh(std::move(result)); return TriangleMesh(std::move(result));
} }
// svg
template<typename Fnc> TriangleMesh try_create_mesh(DataBase& base, const Fnc& was_canceled)
{
const EmbossShape& shape = base.create_shape();
if (shape.shapes.empty())
return {};
double depth = shape.projection.depth / shape.scale;
auto projectZ = std::make_unique<ProjectZ>(depth);
ProjectScale project(std::move(projectZ), shape.scale);
if (was_canceled())
return {};
return TriangleMesh(polygons2model(shape.shapes, project));
}
} // namespace
template<typename Fnc> template<typename Fnc>
TriangleMesh priv::try_create_mesh(DataBase &input, Fnc was_canceled) TriangleMesh try_create_mesh(DataBase &input, const Fnc& was_canceled)
{ {
if (!input.text_lines.empty()) { if (!input.text_lines.empty()) {
TriangleMesh tm = create_mesh_per_glyph(input, was_canceled); TriangleMesh tm = create_mesh_per_glyph(input, was_canceled);
@ -983,17 +923,15 @@ TriangleMesh priv::try_create_mesh(DataBase &input, Fnc was_canceled)
if (!tm.empty()) return tm; if (!tm.empty()) return tm;
} }
ExPolygons shapes = priv::create_shape(input, was_canceled); ExPolygons shapes = create_shape(input, was_canceled);
if (shapes.empty()) return {}; if (shapes.empty()) return {};
if (was_canceled()) return {}; if (was_canceled()) return {};
const FontProp &prop = input.text_configuration.style.prop;
const FontFile &ff = *input.font_file.font_file;
// NOTE: SHAPE_SCALE is applied in ProjectZ // NOTE: SHAPE_SCALE is applied in ProjectZ
double scale = get_shape_scale(prop, ff) / SHAPE_SCALE; double scale = input.shape.scale;
double depth = prop.emboss / scale; double depth = input.shape.projection.depth / scale;
auto projectZ = std::make_unique<ProjectZ>(depth); auto projectZ = std::make_unique<ProjectZ>(depth);
float offset = input.is_outside ? -SAFE_SURFACE_OFFSET : (SAFE_SURFACE_OFFSET - prop.emboss); float offset = input.is_outside ? -SAFE_SURFACE_OFFSET : (SAFE_SURFACE_OFFSET - input.shape.projection.depth);
Transform3d tr = Eigen::Translation<double, 3>(0., 0.,static_cast<double>(offset)) * Eigen::Scaling(scale); Transform3d tr = Eigen::Translation<double, 3>(0., 0.,static_cast<double>(offset)) * Eigen::Scaling(scale);
ProjectTransform project(std::move(projectZ), tr); ProjectTransform project(std::move(projectZ), tr);
if (was_canceled()) return {}; if (was_canceled()) return {};
@ -1001,7 +939,7 @@ TriangleMesh priv::try_create_mesh(DataBase &input, Fnc was_canceled)
} }
template<typename Fnc> template<typename Fnc>
TriangleMesh priv::create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl& ctl) TriangleMesh create_mesh(DataBase &input, const Fnc& was_canceled, Job::Ctl& ctl)
{ {
// It is neccessary to create some shape // It is neccessary to create some shape
// Emboss text window is opened by creation new emboss text object // Emboss text window is opened by creation new emboss text object
@ -1015,8 +953,7 @@ TriangleMesh priv::create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl& ctl)
return {}; return {};
// only info // only info
ctl.call_on_main_thread([]() { ctl.call_on_main_thread([]() {
create_message("It is used default volume for embossed " create_message("It is used default volume for embossed text, try to change text or font to fix it.");
"text, try to change text or font to fix it.");
}); });
} }
@ -1036,7 +973,6 @@ TriangleMesh create_default_mesh()
return triangle_mesh; return triangle_mesh;
} }
namespace{
void update_volume_name(const ModelVolume &volume, const ObjectList *obj_list) void update_volume_name(const ModelVolume &volume, const ObjectList *obj_list)
{ {
if (obj_list == nullptr) if (obj_list == nullptr)
@ -1070,7 +1006,7 @@ void update_volume_name(const ModelVolume &volume, const ObjectList *obj_list)
} }
obj_list->update_name_in_list(object_idx, volume_idx); obj_list->update_name_in_list(object_idx, volume_idx);
} }
}
void update_name_in_list(const ObjectList& object_list, const ModelVolume& volume) void update_name_in_list(const ObjectList& object_list, const ModelVolume& volume)
{ {
const ModelObjectPtrs *objects_ptr = object_list.objects(); const ModelObjectPtrs *objects_ptr = object_list.objects();
@ -1275,14 +1211,10 @@ OrthoProject3d create_emboss_projection(bool is_outside, float emboss, Transform
return OrthoProject3d(from_front_to_back); return OrthoProject3d(from_front_to_back);
} }
namespace {
indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d& tr,const SurfaceVolumeData::ModelSources &sources, DataBase& input, std::function<bool()> was_canceled) { indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d& tr,const SurfaceVolumeData::ModelSources &sources, DataBase& input, std::function<bool()> was_canceled) {
assert(!sources.empty()); assert(!sources.empty());
BoundingBox bb = get_extents(shapes); BoundingBox bb = get_extents(shapes);
const FontFile &ff = *input.font_file.font_file; double shape_scale = input.shape.scale;
const FontProp &fp = input.text_configuration.style.prop;
double shape_scale = get_shape_scale(fp, ff);
const SurfaceVolumeData::ModelSource *biggest = &sources.front(); const SurfaceVolumeData::ModelSource *biggest = &sources.front();
@ -1295,7 +1227,7 @@ indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transfor
Transform3d mesh_tr_inv = s.tr.inverse(); Transform3d mesh_tr_inv = s.tr.inverse();
Transform3d cut_projection_tr = mesh_tr_inv * tr; Transform3d cut_projection_tr = mesh_tr_inv * tr;
std::pair<float, float> z_range{0., 1.}; std::pair<float, float> z_range{0., 1.};
OrthoProject cut_projection = priv::create_projection_for_cut(cut_projection_tr, shape_scale, z_range); OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
// copy only part of source model // copy only part of source model
indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection); indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection);
if (its.indices.empty()) if (its.indices.empty())
@ -1336,9 +1268,9 @@ indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transfor
Transform3d emboss_tr = cut_projection_tr.inverse(); Transform3d emboss_tr = cut_projection_tr.inverse();
BoundingBoxf3 mesh_bb_tr = mesh_bb.transformed(emboss_tr); 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()}; std::pair<float, float> z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()};
OrthoProject cut_projection = priv::create_projection_for_cut(cut_projection_tr, shape_scale, z_range); OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
float projection_ratio = (-z_range.first + priv::safe_extension) / float projection_ratio = (-z_range.first + safe_extension) /
(z_range.second - z_range.first + 2 * priv::safe_extension); (z_range.second - z_range.first + 2 * safe_extension);
ExPolygons shapes_data; // is used only when text is reflected to reverse polygon points order ExPolygons shapes_data; // is used only when text is reflected to reverse polygon points order
const ExPolygons *shapes_ptr = &shapes; const ExPolygons *shapes_ptr = &shapes;
@ -1369,31 +1301,25 @@ indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transfor
if (was_canceled()) return {}; if (was_canceled()) return {};
// !! Projection needs to transform cut // !! Projection needs to transform cut
OrthoProject3d projection = priv::create_emboss_projection(input.is_outside, fp.emboss, emboss_tr, cut); OrthoProject3d projection = create_emboss_projection(input.is_outside, input.shape.projection.depth, emboss_tr, cut);
return cut2model(cut, projection); return cut2model(cut, projection);
} }
TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &input2, std::function<bool()> was_canceled) 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 {};
if (shapes.empty())
throw priv::JobException(_u8L("Font doesn't have any shape for given text.").c_str());
// Precalculate bounding boxes of glyphs // Precalculate bounding boxes of glyphs
// Separate lines of text to vector of Bounds // Separate lines of text to vector of Bounds
const TextConfiguration &tc = input1.text_configuration; const EmbossShape &es = input1.create_shape();
std::wstring ws = boost::nowide::widen(tc.text.c_str()); if (was_canceled()) return {};
assert(get_count_lines(ws) == input1.text_lines.size()); if (es.shapes_with_ids.empty())
size_t count_lines = input1.text_lines.size(); throw JobException(_u8L("Font doesn't have any shape for given text.").c_str());
std::vector<BoundingBoxes> bbs = create_line_bounds(shapes, ws, count_lines);
const FontProp &prop = tc.style.prop; assert(get_count_lines(es.shapes_with_ids) == input1.text_lines.size());
FontFileWithCache &font = input1.font_file; size_t count_lines = input1.text_lines.size();
double shape_scale = get_shape_scale(prop, *font.font_file); std::vector<BoundingBoxes> bbs = create_line_bounds(es.shapes_with_ids, count_lines);
// half of font em size for direction of letter emboss // half of font em size for direction of letter emboss
double em_2_mm = prop.size_in_mm / 2.; double em_2_mm = 5.; // TODO: fix it
int32_t em_2_polygon = static_cast<int32_t>(std::round(scale_(em_2_mm))); 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) size_t s_i_offset = 0; // shape index offset(for next lines)
@ -1401,7 +1327,7 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
for (size_t text_line_index = 0; text_line_index < input1.text_lines.size(); ++text_line_index) { 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 BoundingBoxes &line_bbs = bbs[text_line_index];
const TextLine &line = input1.text_lines[text_line_index]; const TextLine &line = input1.text_lines[text_line_index];
PolygonPoints samples = sample_slice(line, line_bbs, shape_scale); PolygonPoints samples = sample_slice(line, line_bbs, es.scale);
std::vector<double> angles = calculate_angles(em_2_polygon, samples, line.polygon); std::vector<double> angles = calculate_angles(em_2_polygon, samples, line.polygon);
for (size_t i = 0; i < line_bbs.size(); ++i) { for (size_t i = 0; i < line_bbs.size(); ++i) {
@ -1416,7 +1342,7 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
Vec2d offset_vec = unscale(sample.point); // [in mm] Vec2d offset_vec = unscale(sample.point); // [in mm]
auto offset_tr = Eigen::Translation<double, 3>(offset_vec.x(), 0., -offset_vec.y()); auto offset_tr = Eigen::Translation<double, 3>(offset_vec.x(), 0., -offset_vec.y());
ExPolygons &glyph_shape = shapes[s_i_offset + i]; ExPolygons glyph_shape = es.shapes_with_ids[s_i_offset + i].expoly;
assert(get_extents(glyph_shape) == glyph_bb); assert(get_extents(glyph_shape) == glyph_bb);
Point offset(-glyph_bb.center().x(), 0); Point offset(-glyph_bb.center().x(), 0);
@ -1424,7 +1350,7 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
s.translate(offset); s.translate(offset);
Transform3d modify = offset_tr * rotate; Transform3d modify = offset_tr * rotate;
Transform3d tr = input2.text_tr * modify; Transform3d tr = input2.transform * modify;
indexed_triangle_set glyph_its = cut_surface_to_its(glyph_shape, tr, input2.sources, input1, was_canceled); indexed_triangle_set glyph_its = cut_surface_to_its(glyph_shape, tr, input2.sources, input1, was_canceled);
// move letter in volume on the right position // move letter in volume on the right position
its_transform(glyph_its, modify); its_transform(glyph_its, modify);
@ -1440,17 +1366,15 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
if (was_canceled()) return {}; if (was_canceled()) return {};
if (result.empty()) if (result.empty())
throw priv::JobException(_u8L("There is no valid surface for text projection.").c_str()); throw JobException(_u8L("There is no valid surface for text projection.").c_str());
return TriangleMesh(std::move(result)); return TriangleMesh(std::move(result));
} }
} // namespace
// input can't be const - cache of font // input can't be const - cache of font
TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2, std::function<bool()> was_canceled) template<typename Fnc>
TriangleMesh cut_surface(DataBase& input1, const SurfaceVolumeData& input2, const Fnc& was_canceled)
{ {
const FontProp &fp = input1.text_configuration.style.prop; if (!input1.text_lines.empty())
if (fp.per_glyph)
return cut_per_glyph_surface(input1, input2, was_canceled); return cut_per_glyph_surface(input1, input2, was_canceled);
ExPolygons shapes = create_shape(input1, was_canceled); ExPolygons shapes = create_shape(input1, was_canceled);
@ -1458,7 +1382,7 @@ TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2
if (shapes.empty()) if (shapes.empty())
throw JobException(_u8L("Font doesn't have any shape for given text.").c_str()); 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, input1, was_canceled); indexed_triangle_set its = cut_surface_to_its(shapes, input2.transform, input2.sources, input1, was_canceled);
if (was_canceled()) return {}; if (was_canceled()) return {};
if (its.empty()) if (its.empty())
throw JobException(_u8L("There is no valid surface for text projection.").c_str()); throw JobException(_u8L("There is no valid surface for text projection.").c_str());
@ -1537,7 +1461,7 @@ bool start_create_volume_job(Worker &worker,
bool is_outside = volume_type == ModelVolumeType::MODEL_PART; bool is_outside = volume_type == ModelVolumeType::MODEL_PART;
// check that there is not unexpected volume type // check that there is not unexpected volume type
assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER); assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER);
SurfaceVolumeData sfvd{*volume_tr, is_outside, std::move(sources)}; SurfaceVolumeData sfvd{*volume_tr, std::move(sources)};
CreateSurfaceVolumeData surface_data{std::move(sfvd), std::move(data), volume_type, object.id(), gizmo}; CreateSurfaceVolumeData surface_data{std::move(sfvd), std::move(data), volume_type, object.id(), gizmo};
job = std::make_unique<CreateSurfaceVolumeJob>(std::move(surface_data)); job = std::make_unique<CreateSurfaceVolumeJob>(std::move(surface_data));
} }
@ -1667,4 +1591,5 @@ bool start_create_volume_on_surface_job(CreateVolumeParams &input, DataBasePtr d
void create_message(const std::string &message) { void create_message(const std::string &message) {
show_error(nullptr, message.c_str()); show_error(nullptr, message.c_str());
} }
} // namespace } // namespace

View File

@ -4,14 +4,14 @@
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <string> #include <string>
#include "libslic3r/Emboss.hpp" #include <libslic3r/Emboss.hpp>
#include "libslic3r/EmbossShape.hpp" #include <libslic3r/EmbossShape.hpp> // ExPolygonsWithIds
#include "libslic3r/Point.hpp" // Transform3d #include "libslic3r/Point.hpp" // Transform3d
#include "libslic3r/ObjectID.hpp" #include "libslic3r/ObjectID.hpp"
#include "slic3r/GUI/Jobs/EmbossJob.hpp" // Emboss::DataBase
#include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/TextLines.hpp" #include "slic3r/GUI/TextLines.hpp"
#include "Job.hpp" #include "Job.hpp"
// forward declarations // forward declarations
@ -36,31 +36,44 @@ namespace Slic3r::GUI::Emboss {
class DataBase class DataBase
{ {
public: public:
DataBase(const std::string& volume_name, std::shared_ptr<std::atomic<bool>> cancel) : volume_name(volume_name), cancel(std::move(cancel)) {} DataBase(const std::string& volume_name, std::shared_ptr<std::atomic<bool>> cancel)
: volume_name(volume_name), cancel(std::move(cancel)) {}
DataBase(const std::string& volume_name, std::shared_ptr<std::atomic<bool>> cancel, EmbossShape&& shape) DataBase(const std::string& volume_name, std::shared_ptr<std::atomic<bool>> cancel, EmbossShape&& shape)
: volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape)){} : volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape)){}
DataBase(DataBase &&) = default; DataBase(DataBase &&) = default;
virtual ~DataBase() = default; virtual ~DataBase() = default;
// Define projection move
// True (raised) .. move outside from surface
// False (engraved).. move into object
bool is_outside;
// flag that job is canceled
// for time after process.
std::shared_ptr<std::atomic<bool>> cancel;
// Define per letter projection on one text line
// [optional] It is not used when empty
Slic3r::Emboss::TextLines text_lines;
/// <summary> /// <summary>
/// Create shape /// Create shape
/// e.g. Text extract glyphs from font /// e.g. Text extract glyphs from font
/// Not 'const' function because it could modify shape /// Not 'const' function because it could modify shape
/// </summary> /// </summary>
virtual EmbossShape& create_shape() { return shape; }; virtual EmbossShape& create_shape() { return shape; };
/// <summary>
/// Write data how to reconstruct shape to volume
/// </summary>
/// <param name="volume">Data object for store emboss params</param>
virtual void write(ModelVolume &volume) const;
// Define projection move
// True (raised) .. move outside from surface
// False (engraved).. move into object
bool is_outside;
// Define per letter projection on one text line
// [optional] It is not used when empty
Slic3r::Emboss::TextLines text_lines;
// new volume name
std::string volume_name;
// flag that job is canceled
// for time after process.
std::shared_ptr<std::atomic<bool>> cancel;
// shape to emboss
EmbossShape shape;
}; };
/// <summary> /// <summary>
@ -78,22 +91,6 @@ struct DataCreateVolume : public DataBase
// new created volume transformation // new created volume transformation
Transform3d trmat; Transform3d trmat;
};
/// <summary>
/// Write data how to reconstruct shape to volume
/// </summary>
/// <param name="volume">Data object for store emboss params</param>
virtual void write(ModelVolume &volume) const;
// new volume name
std::string volume_name;
// flag that job is canceled
// for time after process.
std::shared_ptr<std::atomic<bool>> cancel;
// shape to emboss
EmbossShape shape;
}; };
using DataBasePtr = std::unique_ptr<DataBase>; using DataBasePtr = std::unique_ptr<DataBase>;

View File

@ -340,6 +340,6 @@ void TextLinesModel::render(const Transform3d &text_world)
double TextLinesModel::calc_line_height(const Slic3r::Emboss::FontFile &ff, const FontProp &fp) double TextLinesModel::calc_line_height(const Slic3r::Emboss::FontFile &ff, const FontProp &fp)
{ {
int line_height = Emboss::get_line_height(ff, fp); // In shape size int line_height = Emboss::get_line_height(ff, fp); // In shape size
double scale = Emboss::get_shape_scale(fp, ff); double scale = Emboss::get_text_shape_scale(fp, ff);
return line_height * scale; return line_height * scale;
} }

View File

@ -531,7 +531,7 @@ TEST_CASE("UndoRedo TextConfiguration serialization", "[Emboss]")
TEST_CASE("UndoRedo EmbossShape serialization", "[Emboss]") TEST_CASE("UndoRedo EmbossShape serialization", "[Emboss]")
{ {
EmbossShape emboss; EmbossShape emboss;
emboss.shapes = {{{0, 0}, {10, 0}, {10, 10}, {0, 10}}, {{5, 5}, {6, 5}, {6, 6}, {5, 6}}}; emboss.shapes_with_ids = {{0, {{{0, 0}, {10, 0}, {10, 10}, {0, 10}}, {{5, 5}, {6, 5}, {6, 6}, {5, 6}}}}};
emboss.scale = 2.; emboss.scale = 2.;
emboss.projection.depth = 5.; emboss.projection.depth = 5.;
emboss.projection.use_surface = true; emboss.projection.use_surface = true;
@ -551,7 +551,7 @@ TEST_CASE("UndoRedo EmbossShape serialization", "[Emboss]")
cereal::BinaryInputArchive iarchive(ss); // Create an input archive cereal::BinaryInputArchive iarchive(ss); // Create an input archive
iarchive(emboss_loaded); iarchive(emboss_loaded);
} }
CHECK(emboss.shapes == emboss_loaded.shapes); CHECK(emboss.shapes_with_ids.front().expoly == emboss_loaded.shapes_with_ids.front().expoly);
CHECK(emboss.scale == emboss_loaded.scale); CHECK(emboss.scale == emboss_loaded.scale);
CHECK(emboss.projection.depth == emboss_loaded.projection.depth); CHECK(emboss.projection.depth == emboss_loaded.projection.depth);
CHECK(emboss.projection.use_surface == emboss_loaded.projection.use_surface); CHECK(emboss.projection.use_surface == emboss_loaded.projection.use_surface);