mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 14:45:57 +08:00
Run able fix after merge(still issue with scale for per glyph transformation)
This commit is contained in:
parent
e831255018
commit
b71cb25163
@ -1270,20 +1270,27 @@ ExPolygons letter2shapes(
|
||||
const int CANCEL_CHECK = 10;
|
||||
} // 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)
|
||||
{
|
||||
std::wstring text_w = boost::nowide::widen(text);
|
||||
std::vector<ExPolygons> vshapes = text2vshapes(font_with_cache, text_w, font_prop, was_canceled);
|
||||
// unify to one expolygon
|
||||
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;
|
||||
ExPolygonsWithIds vshapes = text2vshapes(font_with_cache, text_w, font_prop, was_canceled);
|
||||
return union_ex(vshapes);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -1295,10 +1302,10 @@ namespace {
|
||||
/// Prerequisities: shapes are aligned left top</param>
|
||||
/// <param name="text">To detect end of lines</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());
|
||||
const FontFile &font = *font_with_cache.font_file;
|
||||
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);
|
||||
|
||||
fontinfo_opt font_info_cache;
|
||||
std::vector<ExPolygons> result;
|
||||
ExPolygonsWithIds result;
|
||||
result.reserve(text.size());
|
||||
for (wchar_t letter : text) {
|
||||
if (++counter == CANCEL_CHECK) {
|
||||
@ -1317,7 +1324,8 @@ std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache,
|
||||
if (was_canceled())
|
||||
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));
|
||||
@ -1358,6 +1366,16 @@ unsigned Emboss::get_count_lines(const std::string &text)
|
||||
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){
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (type == no_change)
|
||||
return; // no alignment
|
||||
|
||||
BoundingBox shape_bb;
|
||||
for (const ExPolygons& shape: shapes)
|
||||
shape_bb.merge(get_extents(shape));
|
||||
for (const ExPolygonsWithId& shape: shapes)
|
||||
shape_bb.merge(get_extents(shape.expoly));
|
||||
|
||||
auto get_line_bb = [&](size_t j) {
|
||||
BoundingBox line_bb;
|
||||
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;
|
||||
};
|
||||
|
||||
Point offset(
|
||||
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());
|
||||
for (size_t i = 0; i < shapes.size(); ++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));
|
||||
continue;
|
||||
}
|
||||
ExPolygons &shape = shapes[i];
|
||||
ExPolygons &shape = shapes[i].expoly;
|
||||
for (ExPolygon &s : shape)
|
||||
s.translate(offset);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <admesh/stl.h> // indexed_triangle_set
|
||||
#include "Polygon.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "EmbossShape.hpp" // ExPolygonsWithIds
|
||||
#include "BoundingBox.hpp"
|
||||
#include "TextConfiguration.hpp"
|
||||
|
||||
@ -148,12 +149,14 @@ namespace Emboss
|
||||
/// <param name="font_prop">User defined property of the font</param>
|
||||
/// <param name="was_canceled">Way to interupt processing</param>
|
||||
/// <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;});
|
||||
std::vector<ExPolygons> text2vshapes(FontFileWithCache &font, const std::wstring& 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;});
|
||||
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'
|
||||
unsigned get_count_lines(const std::wstring &ws);
|
||||
unsigned get_count_lines(const std::string &text);
|
||||
unsigned get_count_lines(const ExPolygonsWithIds &shape);
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
|
||||
} // namespace Emboss
|
||||
|
||||
|
||||
ExPolygons union_ex(const ExPolygonsWithIds &shapes);
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_Emboss_hpp_
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <optional>
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <cereal/types/optional.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
#include "Point.hpp" // Transform3d
|
||||
@ -46,13 +47,28 @@ struct EmbossProjection
|
||||
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>
|
||||
/// Contain plane shape information to be able emboss it and edit it
|
||||
/// </summary>
|
||||
struct EmbossShape
|
||||
{
|
||||
// shape defined by integer point consist only by lines not curves
|
||||
ExPolygons shapes;
|
||||
// shapes to to emboss separately over surface
|
||||
ExPolygonsWithIds shapes_with_ids;
|
||||
|
||||
// scale of shape, multiplier to get 3d point in mm from integer shape
|
||||
double scale = 1.;
|
||||
@ -74,16 +90,21 @@ struct EmbossShape
|
||||
// undo / redo stack recovery
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
} // 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_
|
||||
|
@ -3681,7 +3681,9 @@ Transform3d create_fix(const std::optional<Transform3d> &prev, const ModelVolume
|
||||
return *prev * fix_trmat;
|
||||
}
|
||||
|
||||
std::string to_string(const ExPolygons& expolygons){
|
||||
std::string to_string(const ExPolygonsWithIds &shapes)
|
||||
{
|
||||
// TODO: Need to implement
|
||||
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 << 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) << "\" ";
|
||||
|
||||
// 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) {
|
||||
ExPolygons shapes;
|
||||
|
||||
double scale = get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR);
|
||||
|
||||
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);
|
||||
}
|
||||
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)};
|
||||
}
|
||||
|
||||
|
||||
|
@ -345,7 +345,7 @@ bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
|
||||
return false;
|
||||
|
||||
// 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);
|
||||
return start_create_volume(input, std::move(base), mouse_pos);
|
||||
}
|
||||
@ -357,7 +357,7 @@ bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
|
||||
return false;
|
||||
|
||||
// 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);
|
||||
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
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
||||
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.;
|
||||
int line_height_shape_size = get_line_height(ff, fp); // In shape size
|
||||
|
||||
double scale = get_shape_scale(fp, ff);
|
||||
line_offset_mm = third_ascent_shape_size * scale / SHAPE_SCALE;
|
||||
double scale = get_text_shape_scale(fp, ff);
|
||||
line_offset_mm = third_ascent_shape_size * scale / 0.001; // TODO:fix constatnt SHAPE_SCALE
|
||||
line_height_mm = line_height_shape_size * scale;
|
||||
|
||||
if (line_height_mm < 0)
|
||||
@ -1279,7 +1276,7 @@ void GLGizmoEmboss::set_volume_by_selection()
|
||||
// Calculate current angle of up vector
|
||||
assert(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();
|
||||
@ -1322,6 +1319,10 @@ void GLGizmoEmboss::calculate_scale() {
|
||||
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()
|
||||
{
|
||||
// no volume is selected -> selection from right panel
|
||||
@ -1329,27 +1330,26 @@ bool GLGizmoEmboss::process()
|
||||
if (m_volume == nullptr) return false;
|
||||
|
||||
// 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?
|
||||
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()};
|
||||
std::unique_ptr<Job> job = nullptr;
|
||||
|
||||
// check cutting from source mesh
|
||||
bool &use_surface = data.text_configuration.style.prop.use_surface;
|
||||
bool is_object = m_volume->get_object()->volumes.size() == 1;
|
||||
if (use_surface && is_object)
|
||||
bool &use_surface = data.base->shape.projection.use_surface;
|
||||
if (use_surface && m_volume->is_the_only_one_part())
|
||||
use_surface = false;
|
||||
|
||||
assert(!data.text_configuration.style.prop.per_glyph ||
|
||||
get_count_lines(m_text) == m_text_lines.get_lines().size());
|
||||
assert(data.base->text_lines.empty() ||
|
||||
data.base->text_lines.size() == get_count_lines(m_text));
|
||||
|
||||
if (use_surface) {
|
||||
// Model to cut surface from.
|
||||
SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume);
|
||||
SurfaceVolumeData::ModelSources sources = create_volume_sources(*m_volume);
|
||||
if (sources.empty())
|
||||
return false;
|
||||
|
||||
@ -1382,7 +1382,7 @@ bool GLGizmoEmboss::process()
|
||||
queue_job(worker, std::move(job));
|
||||
#else
|
||||
// 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
|
||||
|
||||
// notification is removed befor object is changed by job
|
||||
@ -1390,10 +1390,6 @@ bool GLGizmoEmboss::process()
|
||||
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()
|
||||
{
|
||||
// remove volume when text is empty
|
||||
@ -1506,7 +1502,7 @@ void GLGizmoEmboss::draw_window()
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("##ALLOW_OPEN_NEAR_VOLUME", &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()) {
|
||||
ImGui::SetTooltip("%s", ((m_allow_open_near_volume) ?
|
||||
"Fix settings position":
|
||||
@ -2598,17 +2594,6 @@ void GLGizmoEmboss::draw_height(bool use_inch)
|
||||
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)
|
||||
{
|
||||
double &value = m_style_manager.get_style().projection.depth;
|
||||
@ -2745,6 +2730,7 @@ void GLGizmoEmboss::draw_advanced()
|
||||
}
|
||||
m_imgui->disabled_end(); // !can_use_surface
|
||||
|
||||
FontProp& font_prop = m_style_manager.get_font_prop();
|
||||
bool &per_glyph = font_prop.per_glyph;
|
||||
bool can_use_per_glyph = (per_glyph) ? true : // already used surface must have option to uncheck
|
||||
!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");
|
||||
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;
|
||||
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));
|
||||
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());
|
||||
@ -3013,7 +2999,7 @@ void GLGizmoEmboss::draw_advanced()
|
||||
assert(get_selected_volume(m_parent.get_selection()) == m_volume);
|
||||
const Camera &cam = wxGetApp().plater()->get_camera();
|
||||
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();
|
||||
} else if (ImGui::IsItemHovered()) {
|
||||
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
|
||||
for (auto &input_file : input_files) {
|
||||
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);
|
||||
const FontProp& prop = m_style_manager.get_font_prop();
|
||||
EmbossStyle style{ name, path, EmbossStyle::Type::file_path, prop };
|
||||
@ -3306,14 +3292,16 @@ TextDataBase::TextDataBase(DataBase &&parent,
|
||||
|
||||
EmbossShape &TextDataBase::create_shape()
|
||||
{
|
||||
if (!shape.shapes.empty())
|
||||
if (!shape.shapes_with_ids.empty())
|
||||
return shape;
|
||||
|
||||
// create shape by configuration
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3370,7 +3358,7 @@ std::unique_ptr<DataBase> create_emboss_data_base(const std::string
|
||||
assert(style_manager.get_wx_font().IsOk());
|
||||
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())
|
||||
init_text_lines(text_lines, selection, style_manager);
|
||||
} else
|
||||
@ -3386,9 +3374,12 @@ std::unique_ptr<DataBase> create_emboss_data_base(const std::string
|
||||
// create new shared ptr to cancel new job
|
||||
cancel = std::make_shared<std::atomic<bool>>(false);
|
||||
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();
|
||||
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)
|
||||
@ -3696,6 +3687,8 @@ GuiCfg create_gui_configuration()
|
||||
cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space;
|
||||
|
||||
tr.use_surface = _u8L("Use surface");
|
||||
tr.per_glyph = _u8L("Per glyph orientation");
|
||||
tr.alignment = _u8L("Alignment");
|
||||
tr.char_gap = _u8L("Char gap");
|
||||
tr.line_gap = _u8L("Line gap");
|
||||
tr.boldness = _u8L("Boldness");
|
||||
@ -3707,6 +3700,8 @@ GuiCfg create_gui_configuration()
|
||||
|
||||
float max_advanced_text_width = std::max({
|
||||
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.line_gap.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
|
||||
// 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 =
|
||||
ImVec2(cfg.minimal_window_size.x,
|
||||
cfg.minimal_window_size.y + advance_height);
|
||||
|
@ -483,7 +483,7 @@ void GLGizmoSVG::reset_volume()
|
||||
|
||||
m_volume = nullptr;
|
||||
m_volume_id.id = 0;
|
||||
m_volume_shape.shapes.clear();
|
||||
m_volume_shape.shapes_with_ids.clear();
|
||||
}
|
||||
|
||||
void GLGizmoSVG::calculate_scale() {
|
||||
@ -562,7 +562,7 @@ void GLGizmoSVG::draw_window()
|
||||
ImGui::Unindent(m_gui_cfg->icon_width);
|
||||
|
||||
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
|
||||
process();
|
||||
}
|
||||
@ -597,6 +597,16 @@ void GLGizmoSVG::draw_depth()
|
||||
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()
|
||||
{
|
||||
ImGuiWrapper::text(m_gui_cfg->translations.size);
|
||||
@ -610,7 +620,7 @@ void GLGizmoSVG::draw_size()
|
||||
ImGui::SetNextItemWidth(input_width);
|
||||
|
||||
// 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();
|
||||
|
||||
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";
|
||||
NSVGimage *image = nsvgParseFromFile(file.c_str(), "px", 96.0f);
|
||||
ExPolygons shape = to_expolygons(image);
|
||||
assert(!shape.empty());
|
||||
nsvgDelete(image);
|
||||
return shape;
|
||||
}
|
||||
@ -1018,11 +1029,14 @@ EmbossShape select_shape()
|
||||
int max_level = 10;
|
||||
float scale = static_cast<float>(1 / shape.scale);
|
||||
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 !!!
|
||||
if (shape.shapes.empty())
|
||||
shape.shapes = default_shape();
|
||||
if (expoly.empty())
|
||||
expoly = default_shape();
|
||||
|
||||
unsigned id = 0;
|
||||
shape.shapes_with_ids = {{id, expoly}};
|
||||
|
||||
return shape;
|
||||
}
|
||||
@ -1031,7 +1045,7 @@ DataBasePtr create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel)
|
||||
{
|
||||
EmbossShape shape = select_shape();
|
||||
|
||||
if (shape.shapes.empty())
|
||||
if (shape.shapes_with_ids.empty())
|
||||
// canceled selection of SVG file
|
||||
return nullptr;
|
||||
|
||||
|
@ -160,7 +160,6 @@ 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);
|
||||
|
||||
// create sure that emboss object is bigger than source object [in mm]
|
||||
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)); }
|
||||
|
||||
void CreateVolumeJob::process(Ctl &ctl) {
|
||||
if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossCreateVolumeJob.");
|
||||
auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); };
|
||||
m_result = priv::create_mesh(m_input, was_canceled, ctl);
|
||||
//m_result = create_mesh(*m_input.base, was_canceled(ctl, *m_input.base), ctl); // svg
|
||||
if (!check(m_input))
|
||||
throw std::runtime_error("Bad input data for EmbossCreateVolumeJob.");
|
||||
m_result = create_mesh(*m_input.base, was_canceled(ctl, *m_input.base), ctl);
|
||||
}
|
||||
void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||
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);
|
||||
if (was_canceled()) return;
|
||||
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)
|
||||
@ -658,10 +656,7 @@ bool start_update_volume(DataUpdate &&data, const ModelVolume &volume, const Sel
|
||||
volume_tr *= Eigen::Translation<double, 3>(*offset);
|
||||
}
|
||||
|
||||
bool is_outside = volume.is_model_part();
|
||||
// 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)}};
|
||||
UpdateSurfaceVolumeData surface_data{std::move(data), {volume_tr, std::move(sources)}};
|
||||
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
|
||||
} else {
|
||||
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();
|
||||
assert(!input.volume_name.empty());
|
||||
res &= !input.volume_name.empty();
|
||||
const FontProp& prop = input.text_configuration.style.prop;
|
||||
assert(prop.per_glyph == !input.text_lines.empty());
|
||||
res &= prop.per_glyph == !input.text_lines.empty();
|
||||
if (prop.per_glyph) {
|
||||
assert(get_count_lines(input.text_configuration.text) == input.text_lines.size());
|
||||
res &= get_count_lines(input.text_configuration.text) == input.text_lines.size();
|
||||
}
|
||||
//const FontProp& prop = input.text_configuration.style.prop;
|
||||
//assert(prop.per_glyph == !input.text_lines.empty());
|
||||
//res &= prop.per_glyph == !input.text_lines.empty();
|
||||
//if (prop.per_glyph) {
|
||||
// assert(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;
|
||||
}
|
||||
|
||||
@ -791,70 +786,34 @@ bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread)
|
||||
}
|
||||
|
||||
template<typename Fnc>
|
||||
ExPolygons priv::create_shape(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 {};
|
||||
|
||||
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;
|
||||
ExPolygons create_shape(DataBase &input, Fnc was_canceled) {
|
||||
const EmbossShape &es = input.create_shape();
|
||||
return union_ex(es.shapes_with_ids);
|
||||
}
|
||||
|
||||
//#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)
|
||||
std::vector<BoundingBoxes> create_line_bounds(const ExPolygonsWithIds &shapes, 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));
|
||||
count_lines = get_count_lines(shapes);
|
||||
assert(count_lines == get_count_lines(shapes));
|
||||
|
||||
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];
|
||||
for (const ExPolygonsWithId &shape_id: shapes) {
|
||||
const ExPolygons &shape = shape_id.expoly;
|
||||
BoundingBox bb;
|
||||
if (!shape.empty()) {
|
||||
bb = get_extents(shape);
|
||||
}
|
||||
BoundingBoxes &line_bbs = result[text_line_index];
|
||||
line_bbs.push_back(bb);
|
||||
if (text[s_i] == '\n'){
|
||||
if (shape_id.id == ENTER_UNICODE) {
|
||||
// skip enters on beginig and tail
|
||||
++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
|
||||
static_assert(std::is_same<Point::coord_type, int32_t>());
|
||||
|
||||
std::vector<ExPolygons> shapes = priv::create_shapes(input, was_canceled);
|
||||
if (shapes.empty())
|
||||
const EmbossShape &shape = input.create_shape();
|
||||
if (shape.shapes_with_ids.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());
|
||||
assert(get_count_lines(shape.shapes_with_ids) == 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;
|
||||
std::vector<BoundingBoxes> bbs = create_line_bounds(shape.shapes_with_ids, count_lines);
|
||||
|
||||
double projec_scale = shape.scale / 0.001; // SHAPE_SCALE;
|
||||
double depth = shape.projection.depth / projec_scale;
|
||||
auto scale_tr = Eigen::Scaling(projec_scale);
|
||||
|
||||
// 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)));
|
||||
|
||||
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) {
|
||||
const BoundingBoxes &line_bbs = bbs[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);
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
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);
|
||||
if (prop.distance.has_value())
|
||||
surface_offset += *prop.distance;
|
||||
Vec2d to_zero_vec = letter_bb.center().cast<double>() * shape.scale; // [in mm]
|
||||
float surface_offset = input.is_outside ? -SAFE_SURFACE_OFFSET : (-shape.projection.depth + SAFE_SURFACE_OFFSET);
|
||||
|
||||
// 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));
|
||||
|
||||
@ -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());
|
||||
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);
|
||||
auto projectZ = std::make_unique<ProjectZ>(depth);
|
||||
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));
|
||||
}
|
||||
|
||||
// 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>
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
|
||||
ExPolygons shapes = priv::create_shape(input, was_canceled);
|
||||
ExPolygons shapes = create_shape(input, was_canceled);
|
||||
if (shapes.empty()) 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
|
||||
double scale = get_shape_scale(prop, ff) / SHAPE_SCALE;
|
||||
double depth = prop.emboss / scale;
|
||||
double scale = input.shape.scale;
|
||||
double depth = input.shape.projection.depth / scale;
|
||||
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);
|
||||
ProjectTransform project(std::move(projectZ), tr);
|
||||
if (was_canceled()) return {};
|
||||
@ -1001,7 +939,7 @@ TriangleMesh priv::try_create_mesh(DataBase &input, Fnc was_canceled)
|
||||
}
|
||||
|
||||
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
|
||||
// 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 {};
|
||||
// only info
|
||||
ctl.call_on_main_thread([]() {
|
||||
create_message("It is used default volume for embossed "
|
||||
"text, try to change text or font to fix it.");
|
||||
create_message("It is used default volume for embossed text, try to change text or font to fix it.");
|
||||
});
|
||||
}
|
||||
|
||||
@ -1036,7 +973,6 @@ TriangleMesh create_default_mesh()
|
||||
return triangle_mesh;
|
||||
}
|
||||
|
||||
namespace{
|
||||
void update_volume_name(const ModelVolume &volume, const ObjectList *obj_list)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void update_name_in_list(const ObjectList& object_list, const ModelVolume& volume)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
assert(!sources.empty());
|
||||
BoundingBox bb = get_extents(shapes);
|
||||
const FontFile &ff = *input.font_file.font_file;
|
||||
const FontProp &fp = input.text_configuration.style.prop;
|
||||
double shape_scale = get_shape_scale(fp, ff);
|
||||
double shape_scale = input.shape.scale;
|
||||
|
||||
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 cut_projection_tr = mesh_tr_inv * tr;
|
||||
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
|
||||
indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection);
|
||||
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();
|
||||
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 = 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);
|
||||
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);
|
||||
|
||||
ExPolygons shapes_data; // is used only when text is reflected to reverse polygon points order
|
||||
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 {};
|
||||
|
||||
// !! 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);
|
||||
}
|
||||
|
||||
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
|
||||
// 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 EmbossShape &es = input1.create_shape();
|
||||
if (was_canceled()) return {};
|
||||
if (es.shapes_with_ids.empty())
|
||||
throw JobException(_u8L("Font doesn't have any shape for given text.").c_str());
|
||||
|
||||
const FontProp &prop = tc.style.prop;
|
||||
FontFileWithCache &font = input1.font_file;
|
||||
double shape_scale = get_shape_scale(prop, *font.font_file);
|
||||
|
||||
assert(get_count_lines(es.shapes_with_ids) == input1.text_lines.size());
|
||||
size_t count_lines = input1.text_lines.size();
|
||||
std::vector<BoundingBoxes> bbs = create_line_bounds(es.shapes_with_ids, count_lines);
|
||||
|
||||
// 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)));
|
||||
|
||||
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) {
|
||||
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);
|
||||
PolygonPoints samples = sample_slice(line, line_bbs, es.scale);
|
||||
std::vector<double> angles = calculate_angles(em_2_polygon, samples, line.polygon);
|
||||
|
||||
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]
|
||||
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);
|
||||
|
||||
Point offset(-glyph_bb.center().x(), 0);
|
||||
@ -1424,7 +1350,7 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
|
||||
s.translate(offset);
|
||||
|
||||
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);
|
||||
// move letter in volume on the right position
|
||||
its_transform(glyph_its, modify);
|
||||
@ -1440,17 +1366,15 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
|
||||
|
||||
if (was_canceled()) return {};
|
||||
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));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// 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 (fp.per_glyph)
|
||||
if (!input1.text_lines.empty())
|
||||
return cut_per_glyph_surface(input1, input2, 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())
|
||||
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 (its.empty())
|
||||
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;
|
||||
// check that there is not unexpected volume type
|
||||
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};
|
||||
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) {
|
||||
show_error(nullptr, message.c_str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -4,14 +4,14 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "libslic3r/Emboss.hpp"
|
||||
#include "libslic3r/EmbossShape.hpp"
|
||||
#include <libslic3r/Emboss.hpp>
|
||||
#include <libslic3r/EmbossShape.hpp> // ExPolygonsWithIds
|
||||
#include "libslic3r/Point.hpp" // Transform3d
|
||||
#include "libslic3r/ObjectID.hpp"
|
||||
|
||||
#include "slic3r/GUI/Jobs/EmbossJob.hpp" // Emboss::DataBase
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "slic3r/GUI/TextLines.hpp"
|
||||
|
||||
#include "Job.hpp"
|
||||
|
||||
// forward declarations
|
||||
@ -36,31 +36,44 @@ namespace Slic3r::GUI::Emboss {
|
||||
class DataBase
|
||||
{
|
||||
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)
|
||||
: volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape)){}
|
||||
DataBase(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>
|
||||
/// Create shape
|
||||
/// e.g. Text extract glyphs from font
|
||||
/// Not 'const' function because it could modify shape
|
||||
/// </summary>
|
||||
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>
|
||||
@ -78,22 +91,6 @@ struct DataCreateVolume : public DataBase
|
||||
|
||||
// new created volume transformation
|
||||
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>;
|
||||
|
||||
|
@ -340,6 +340,6 @@ void TextLinesModel::render(const Transform3d &text_world)
|
||||
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
|
||||
double scale = Emboss::get_shape_scale(fp, ff);
|
||||
double scale = Emboss::get_text_shape_scale(fp, ff);
|
||||
return line_height * scale;
|
||||
}
|
||||
|
@ -531,7 +531,7 @@ TEST_CASE("UndoRedo TextConfiguration serialization", "[Emboss]")
|
||||
TEST_CASE("UndoRedo EmbossShape serialization", "[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.projection.depth = 5.;
|
||||
emboss.projection.use_surface = true;
|
||||
@ -551,7 +551,7 @@ TEST_CASE("UndoRedo EmbossShape serialization", "[Emboss]")
|
||||
cereal::BinaryInputArchive iarchive(ss); // Create an input archive
|
||||
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.projection.depth == emboss_loaded.projection.depth);
|
||||
CHECK(emboss.projection.use_surface == emboss_loaded.projection.use_surface);
|
||||
|
Loading…
x
Reference in New Issue
Block a user