Merge branch 'fs_SPE-2117'

This commit is contained in:
Lukas Matena 2024-06-20 16:01:49 +02:00
commit 87d7f9fe7f
10 changed files with 871 additions and 547 deletions

View File

@ -775,14 +775,13 @@ const Glyph* get_glyph(
unsigned int font_index = font_prop.collection_number.value_or(0); unsigned int font_index = font_prop.collection_number.value_or(0);
if (!is_valid(font, font_index)) return nullptr; if (!is_valid(font, font_index)) return nullptr;
if (!font_info_opt.has_value()) { if (!font_info_opt.has_value()) {
font_info_opt = load_font_info(font.data->data(), font_index);
font_info_opt = load_font_info(font.data->data(), font_index);
// can load font info? // can load font info?
if (!font_info_opt.has_value()) return nullptr; if (!font_info_opt.has_value()) return nullptr;
} }
float flatness = font.infos[font_index].ascent * RESOLUTION / font_prop.size_in_mm; float flatness = font.infos[font_index].unit_per_em / font_prop.size_in_mm * RESOLUTION;
// Fix for very small flatness because it create huge amount of points from curve // Fix for very small flatness because it create huge amount of points from curve
if (flatness < RESOLUTION) flatness = RESOLUTION; if (flatness < RESOLUTION) flatness = RESOLUTION;
@ -1066,11 +1065,10 @@ std::unique_ptr<FontFile> Emboss::create_font_file(
int ascent, descent, linegap; int ascent, descent, linegap;
stbtt_GetFontVMetrics(info, &ascent, &descent, &linegap); stbtt_GetFontVMetrics(info, &ascent, &descent, &linegap);
float pixels = 1000.; // value is irelevant float pixels = 1000.; // value is irelevant
float em_pixels = stbtt_ScaleForMappingEmToPixels(info, pixels); float em_pixels = stbtt_ScaleForMappingEmToPixels(info, pixels);
int units_per_em = static_cast<int>(std::round(pixels / em_pixels)); int unit_per_em = static_cast<int>(std::round(pixels / em_pixels));
infos.emplace_back(FontFile::Info{ascent, descent, linegap, unit_per_em});
infos.emplace_back(FontFile::Info{ascent, descent, linegap, units_per_em});
} }
return std::make_unique<FontFile>(std::move(data), std::move(infos)); return std::make_unique<FontFile>(std::move(data), std::move(infos));
} }
@ -1200,15 +1198,13 @@ int Emboss::get_line_height(const FontFile &font, const FontProp &prop) {
namespace { namespace {
ExPolygons letter2shapes( ExPolygons letter2shapes(
wchar_t letter, Point &cursor, FontFileWithCache &font_with_cache, const FontProp &font_prop, fontinfo_opt& font_info_cache) wchar_t letter,
{ Point &cursor,
assert(font_with_cache.has_value()); const FontFile &font,
if (!font_with_cache.has_value()) Glyphs &cache,
return {}; const FontProp &font_prop,
fontinfo_opt &font_info_cache
Glyphs &cache = *font_with_cache.cache; ) {
const FontFile &font = *font_with_cache.font_file;
if (letter == '\n') { if (letter == '\n') {
cursor.x() = 0; cursor.x() = 0;
// 2d shape has opposit direction of y // 2d shape has opposit direction of y
@ -1322,12 +1318,16 @@ void align_shape(ExPolygonsWithIds &shapes, const std::wstring &text, const Font
ExPolygonsWithIds 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());
if (!font_with_cache.has_value())
return {};
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);
if (!is_valid(font, font_index)) if (!is_valid(font, font_index))
return {}; return {};
unsigned counter = 0; std::shared_ptr<Glyphs> cache = font_with_cache.cache; // copy pointer
unsigned counter = CANCEL_CHECK-1; // it is needed to validate using of cache
Point cursor(0, 0); Point cursor(0, 0);
fontinfo_opt font_info_cache; fontinfo_opt font_info_cache;
@ -1340,7 +1340,7 @@ ExPolygonsWithIds Emboss::text2vshapes(FontFileWithCache &font_with_cache, const
return {}; return {};
} }
unsigned id = static_cast<unsigned>(letter); unsigned id = static_cast<unsigned>(letter);
result.push_back({id, letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_cache)}); result.push_back({id, letter2shapes(letter, cursor, font, *cache, font_prop, font_info_cache)});
} }
align_shape(result, text, font_prop, font); align_shape(result, text, font_prop, font);
@ -1887,12 +1887,27 @@ double Emboss::calculate_angle(int32_t distance, PolygonPoint polygon_point, con
return std::atan2(norm_d.y(), norm_d.x()); return std::atan2(norm_d.y(), norm_d.x());
} }
std::vector<double> Emboss::calculate_angles(int32_t distance, const PolygonPoints& polygon_points, const Polygon &polygon) std::vector<double> Emboss::calculate_angles(const BoundingBoxes &glyph_sizes, const PolygonPoints& polygon_points, const Polygon &polygon)
{ {
const int32_t default_distance = static_cast<int32_t>(std::round(scale_(5.)));
const int32_t min_distance = static_cast<int32_t>(std::round(scale_(.1)));
std::vector<double> result; std::vector<double> result;
result.reserve(polygon_points.size()); result.reserve(polygon_points.size());
for(const PolygonPoint& pp: polygon_points) assert(glyph_sizes.size() == polygon_points.size());
result.emplace_back(calculate_angle(distance, pp, polygon)); if (glyph_sizes.size() != polygon_points.size()) {
// only backup solution should not be used
for (const PolygonPoint &pp : polygon_points)
result.emplace_back(calculate_angle(default_distance, pp, polygon));
return result;
}
for (size_t i = 0; i < polygon_points.size(); i++) {
int32_t distance = glyph_sizes[i].size().x() / 2;
if (distance < min_distance) // too small could lead to false angle
distance = default_distance;
result.emplace_back(calculate_angle(distance, polygon_points[i], polygon));
}
return result; return result;
} }

View File

@ -282,8 +282,6 @@ namespace Emboss
class IProjection : public IProject3d class IProjection : public IProject3d
{ {
public: public:
virtual ~IProjection() = default;
/// <summary> /// <summary>
/// convert 2d point to 3d points /// convert 2d point to 3d points
/// </summary> /// </summary>
@ -461,7 +459,11 @@ namespace Emboss
/// <param name="polygon">Polygon know neighbor of point</param> /// <param name="polygon">Polygon know neighbor of point</param>
/// <returns>angle(atan2) of normal in polygon point</returns> /// <returns>angle(atan2) of normal in polygon point</returns>
double calculate_angle(int32_t distance, PolygonPoint polygon_point, const Polygon &polygon); double calculate_angle(int32_t distance, PolygonPoint polygon_point, const Polygon &polygon);
std::vector<double> calculate_angles(int32_t distance, const PolygonPoints& polygon_points, const Polygon &polygon); std::vector<double> calculate_angles(
const BoundingBoxes &glyph_sizes,
const PolygonPoints &polygon_points,
const Polygon &polygon
);
} // namespace Emboss } // namespace Emboss

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,9 @@ namespace Slic3r{
class AppConfig; class AppConfig;
class GLVolume; class GLVolume;
enum class ModelVolumeType : int; enum class ModelVolumeType : int;
namespace GUI::Emboss {
struct CreateVolumeParams;
}
} }
namespace Slic3r::GUI { namespace Slic3r::GUI {
@ -64,7 +67,6 @@ public:
/// <returns>True on success start job otherwise False</returns> /// <returns>True on success start job otherwise False</returns>
bool do_mirror(size_t axis); bool do_mirror(size_t axis);
/// <summary> /// <summary>
/// Call on change inside of object conatining projected volume /// Call on change inside of object conatining projected volume
/// </summary> /// </summary>
@ -114,7 +116,7 @@ private:
void reset_volume(); void reset_volume();
// create volume from text - main functionality // create volume from text - main functionality
bool process(bool make_snapshot = true); bool process(bool make_snapshot = true, std::optional<Transform3d> volume_transformation = std::nullopt);
void close(); void close();
void draw_window(); void draw_window();
void draw_text_input(); void draw_text_input();
@ -139,6 +141,17 @@ private:
bool draw_bold_button(); bool draw_bold_button();
void draw_advanced(); void draw_advanced();
void draw_use_surface();
void draw_per_glyph();
void draw_align();
void draw_char_gap();
void draw_line_gap();
void draw_boldness();
void draw_skew();
void draw_rotation();
void draw_surface_distance();
bool select_facename(const wxString& facename); bool select_facename(const wxString& facename);
template<typename T> bool rev_input_mm(const std::string &name, T &value, const T *default_value, template<typename T> bool rev_input_mm(const std::string &name, T &value, const T *default_value,
@ -152,12 +165,10 @@ private:
template<typename T> bool rev_input(const std::string &name, T &value, const T *default_value, template<typename T> bool rev_input(const std::string &name, T &value, const T *default_value,
const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags = 0) const; const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags = 0) const;
bool rev_checkbox(const std::string &name, bool &value, const bool* default_value, const std::string &undo_tooltip) const; bool rev_checkbox(const std::string &name, bool &value, const bool* default_value, const std::string &undo_tooltip) const;
bool rev_slider(const std::string &name, std::optional<int>& value, const std::optional<int> *default_value,
const std::string &undo_tooltip, int v_min, int v_max, const std::string &format, const wxString &tooltip) const;
bool rev_slider(const std::string &name, std::optional<float>& value, const std::optional<float> *default_value, bool rev_slider(const std::string &name, std::optional<float>& value, const std::optional<float> *default_value,
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip) const; const std::string &undo_tooltip, const MinMax<float>& min_max, const std::string &format, const wxString &tooltip) const;
bool rev_slider(const std::string &name, float &value, const float *default_value, bool rev_slider(const std::string &name, float &value, const float *default_value,
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip) const; const std::string &undo_tooltip, const MinMax<float>& min_max, const std::string &format, const wxString &tooltip) const;
template<typename T, typename Draw> bool revertible(const std::string &name, T &value, const T *default_value, template<typename T, typename Draw> bool revertible(const std::string &name, T &value, const T *default_value,
const std::string &undo_tooltip, float undo_offset, Draw draw) const; const std::string &undo_tooltip, float undo_offset, Draw draw) const;
@ -176,6 +187,9 @@ private:
void create_notification_not_valid_font(const std::string& text); void create_notification_not_valid_font(const std::string& text);
void remove_notification_not_valid_font(); void remove_notification_not_valid_font();
// initialize data for create volume in job
Emboss::CreateVolumeParams create_input(ModelVolumeType volume_type);
struct GuiCfg; struct GuiCfg;
std::unique_ptr<const GuiCfg> m_gui_cfg; std::unique_ptr<const GuiCfg> m_gui_cfg;
@ -234,6 +248,7 @@ private:
// For text on scaled objects // For text on scaled objects
std::optional<float> m_scale_height; std::optional<float> m_scale_height;
std::optional<float> m_scale_depth; std::optional<float> m_scale_depth;
std::optional<float> m_scale_width;
void calculate_scale(); void calculate_scale();
// drawing icons // drawing icons

View File

@ -118,15 +118,6 @@ std::string get_file_name(const std::string &file_path);
/// <returns>Name for volume</returns> /// <returns>Name for volume</returns>
std::string volume_name(const EmbossShape& shape); std::string volume_name(const EmbossShape& shape);
/// <summary>
/// Create input for volume creation
/// </summary>
/// <param name="canvas">parent of gizmo</param>
/// <param name="raycaster">Keep scene</param>
/// <param name="volume_type">Type of volume to be created</param>
/// <returns>Params</returns>
CreateVolumeParams create_input(GLCanvas3D &canvas, RaycastManager &raycaster, ModelVolumeType volume_type);
enum class IconType : unsigned { enum class IconType : unsigned {
reset_value, reset_value,
reset_value_hover, reset_value_hover,
@ -198,33 +189,38 @@ struct GLGizmoSVG::GuiCfg: public ::GuiCfg{};
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos) bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos)
{ {
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type); CreateVolumeParams input = create_input(volume_type);
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type); if (!input.data) return false; // Uninterpretable svg
if (!base) return false; // Uninterpretable svg return start_create_volume(input, mouse_pos);
return start_create_volume(input, std::move(base), mouse_pos);
} }
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type) bool GLGizmoSVG::create_volume(ModelVolumeType volume_type)
{ {
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type); CreateVolumeParams input = create_input(volume_type);
DataBasePtr base = create_emboss_data_base(m_job_cancel,volume_type); if (!input.data) return false; // Uninterpretable svg
if (!base) return false; // Uninterpretable svg return start_create_volume_without_position(input);
return start_create_volume_without_position(input, std::move(base));
} }
bool GLGizmoSVG::create_volume(std::string_view svg_file, ModelVolumeType volume_type){ bool GLGizmoSVG::create_volume(std::string_view svg_file, ModelVolumeType volume_type){
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type); CreateVolumeParams input = create_input(volume_type, svg_file);
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type, svg_file); if (!input.data) return false; // Uninterpretable svg
if (!base) return false; // Uninterpretable svg return start_create_volume_without_position(input);
return start_create_volume_without_position(input, std::move(base));
} }
bool GLGizmoSVG::create_volume(std::string_view svg_file, const Vec2d &mouse_pos, ModelVolumeType volume_type) bool GLGizmoSVG::create_volume(std::string_view svg_file, const Vec2d &mouse_pos, ModelVolumeType volume_type)
{ {
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type); CreateVolumeParams input = create_input(volume_type, svg_file);
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type, svg_file); if (!input.data) return false; // Uninterpretable svg
if (!base) return false; // Uninterpretable svg return start_create_volume(input, mouse_pos);
return start_create_volume(input, std::move(base), mouse_pos); }
CreateVolumeParams GLGizmoSVG::create_input(ModelVolumeType volume_type, std::string_view svg_filepath) {
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type, svg_filepath);
auto gizmo = static_cast<unsigned char>(GLGizmosManager::Svg);
const GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent);
Plater *plater = wxGetApp().plater();
return CreateVolumeParams{std::move(base), m_parent, plater->get_camera(), plater->build_volume(),
plater->get_ui_job_worker(), volume_type, m_raycast_manager, gizmo, gl_volume};
} }
bool GLGizmoSVG::is_svg(const ModelVolume &volume) { bool GLGizmoSVG::is_svg(const ModelVolume &volume) {
@ -2117,15 +2113,6 @@ std::string volume_name(const EmbossShape &shape)
return "SVG shape"; return "SVG shape";
} }
CreateVolumeParams create_input(GLCanvas3D &canvas, RaycastManager& raycaster, ModelVolumeType volume_type)
{
auto gizmo = static_cast<unsigned char>(GLGizmosManager::Svg);
const GLVolume *gl_volume = get_first_hovered_gl_volume(canvas);
Plater *plater = wxGetApp().plater();
return CreateVolumeParams{canvas, plater->get_camera(), plater->build_volume(),
plater->get_ui_job_worker(), volume_type, raycaster, gizmo, gl_volume};
}
GuiCfg create_gui_configuration() { GuiCfg create_gui_configuration() {
GuiCfg cfg; // initialize by default values; GuiCfg cfg; // initialize by default values;

View File

@ -24,6 +24,9 @@
namespace Slic3r{ namespace Slic3r{
class ModelVolume; class ModelVolume;
enum class ModelVolumeType : int; enum class ModelVolumeType : int;
namespace GUI::Emboss {
struct CreateVolumeParams;
}
} }
namespace Slic3r::GUI { namespace Slic3r::GUI {
@ -133,6 +136,8 @@ private:
void volume_transformation_changed(); void volume_transformation_changed();
Emboss::CreateVolumeParams create_input(ModelVolumeType volume_type, std::string_view svg_filepath = "");
struct GuiCfg; struct GuiCfg;
std::unique_ptr<const GuiCfg> m_gui_cfg; std::unique_ptr<const GuiCfg> m_gui_cfg;

View File

@ -197,7 +197,7 @@ TriangleMesh create_default_mesh();
/// <param name="mesh">New mesh data</param> /// <param name="mesh">New mesh data</param>
/// <param name="data">Text configuration, ...</param> /// <param name="data">Text configuration, ...</param>
/// <param name="mesh">Transformation of volume</param> /// <param name="mesh">Transformation of volume</param>
void update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3d *tr = nullptr); void final_update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3d *tr = nullptr);
/// <summary> /// <summary>
/// Update name in right panel /// Update name in right panel
@ -414,7 +414,7 @@ void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr)
{ {
if (!::finalize(canceled, eptr, *m_input.base)) if (!::finalize(canceled, eptr, *m_input.base))
return; return;
::update_volume(std::move(m_result), m_input); ::final_update_volume(std::move(m_result), m_input);
} }
void UpdateJob::update_volume(ModelVolume *volume, TriangleMesh &&mesh, const DataBase &base) void UpdateJob::update_volume(ModelVolume *volume, TriangleMesh &&mesh, const DataBase &base)
@ -496,7 +496,7 @@ void UpdateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr)
// when start using surface it is wanted to move text origin on surface of model // when start using surface it is wanted to move text origin on surface of model
// also when repeteadly move above surface result position should match // also when repeteadly move above surface result position should match
::update_volume(std::move(m_result), m_input, &m_input.transform); ::final_update_volume(std::move(m_result), m_input, &m_input.transform);
} }
namespace { namespace {
@ -538,11 +538,11 @@ const GLVolume *find_closest(
/// <summary> /// <summary>
/// Start job for add object with text into scene /// Start job for add object with text into scene
/// </summary> /// </summary>
/// <param name="input">Contain worker, build shape, gizmo</param> /// <param name="input">Contain worker, build shape, gizmo,
/// <param name="emboss_data">Define params for create volume</param> /// emboss_data is moved out soo it can't be const</param>
/// <param name="coor">Screen coordinat, where to create new object laying on bed</param> /// <param name="coor">Screen coordinat, where to create new object laying on bed</param>
/// <returns>True when can add job to worker otherwise FALSE</returns> /// <returns>True when can add job to worker otherwise FALSE</returns>
bool start_create_object_job(const CreateVolumeParams &input, DataBasePtr emboss_data, const Vec2d &coor); bool start_create_object_job(CreateVolumeParams &input, const Vec2d &coor);
/// <summary> /// <summary>
/// Start job to create volume on the surface of object /// Start job to create volume on the surface of object
@ -553,7 +553,7 @@ bool start_create_object_job(const CreateVolumeParams &input, DataBasePtr emboss
/// <param name="try_no_coor">True .. try to create volume without screen_coor, /// <param name="try_no_coor">True .. try to create volume without screen_coor,
/// False .. </param> /// False .. </param>
/// <returns>Nullptr when job is sucessfully add to worker otherwise return data to be processed different way</returns> /// <returns>Nullptr when job is sucessfully add to worker otherwise return data to be processed different way</returns>
bool start_create_volume_on_surface_job(CreateVolumeParams &input, DataBasePtr data, const Vec2d &screen_coor, bool try_no_coor); bool start_create_volume_on_surface_job(CreateVolumeParams &input, const Vec2d &screen_coor, bool try_no_coor);
} // namespace } // namespace
@ -568,25 +568,25 @@ SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume &text_vo
return ::create_sources(volumes, text_volume.id().id); return ::create_sources(volumes, text_volume.id().id);
} }
bool start_create_volume(CreateVolumeParams &input, DataBasePtr data, const Vec2d &mouse_pos) bool start_create_volume(CreateVolumeParams &input, const Vec2d &mouse_pos)
{ {
if (data == nullptr) if (input.data == nullptr)
return false; return false;
if (!check(input)) if (!check(input))
return false; return false;
if (input.gl_volume == nullptr) if (input.gl_volume == nullptr)
// object is not under mouse position soo create object on plater // object is not under mouse position soo create object on plater
return ::start_create_object_job(input, std::move(data), mouse_pos); return ::start_create_object_job(input, mouse_pos);
bool try_no_coor = true; bool try_no_coor = true;
return ::start_create_volume_on_surface_job(input, std::move(data), mouse_pos, try_no_coor); return ::start_create_volume_on_surface_job(input, mouse_pos, try_no_coor);
} }
bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr data) bool start_create_volume_without_position(CreateVolumeParams &input)
{ {
assert(data != nullptr); assert(input.data != nullptr);
if (data == nullptr) if (input.data == nullptr)
return false; return false;
if (!check(input)) if (!check(input))
return false; return false;
@ -604,17 +604,17 @@ bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr
static_cast<size_t>(object_idx) >= objects.size()) static_cast<size_t>(object_idx) >= objects.size())
// create Object on center of screen // create Object on center of screen
// when ray throw center of screen not hit bed it create object on center of bed // when ray throw center of screen not hit bed it create object on center of bed
return ::start_create_object_job(input, std::move(data), screen_center); return ::start_create_object_job(input, screen_center);
// create volume inside of selected object // create volume inside of selected object
Vec2d coor; Vec2d coor;
const Camera &camera = wxGetApp().plater()->get_camera(); const Camera &camera = wxGetApp().plater()->get_camera();
input.gl_volume = ::find_closest(selection, screen_center, camera, objects, &coor); input.gl_volume = ::find_closest(selection, screen_center, camera, objects, &coor);
if (input.gl_volume == nullptr) if (input.gl_volume == nullptr)
return ::start_create_object_job(input, std::move(data), screen_center); return ::start_create_object_job(input, screen_center);
bool try_no_coor = false; bool try_no_coor = false;
return ::start_create_volume_on_surface_job(input, std::move(data), coor, try_no_coor); return ::start_create_volume_on_surface_job(input, coor, try_no_coor);
} }
#ifdef EXECUTE_UPDATE_ON_MAIN_THREAD #ifdef EXECUTE_UPDATE_ON_MAIN_THREAD
@ -850,18 +850,13 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
double depth = shape.projection.depth / shape.scale; double depth = shape.projection.depth / shape.scale;
auto scale_tr = Eigen::Scaling(shape.scale); auto scale_tr = Eigen::Scaling(shape.scale);
// half of font em size for direction of letter emboss
// 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) size_t s_i_offset = 0; // shape index offset(for next lines)
indexed_triangle_set result; indexed_triangle_set result;
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(line_bbs, samples, line.polygon);
for (size_t i = 0; i < line_bbs.size(); ++i) { for (size_t i = 0; i < line_bbs.size(); ++i) {
const BoundingBox &letter_bb = line_bbs[i]; const BoundingBox &letter_bb = line_bbs[i];
@ -1020,7 +1015,7 @@ void update_name_in_list(const ObjectList& object_list, const ModelVolume& volum
object_list.update_name_in_list(object_index, volume_index); object_list.update_name_in_list(object_index, volume_index);
} }
void update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3d *tr) void final_update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3d *tr)
{ {
// for sure that some object will be created // for sure that some object will be created
if (mesh.its.empty()) if (mesh.its.empty())
@ -1044,7 +1039,12 @@ void update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3
if (volume == nullptr) if (volume == nullptr)
return; return;
if (tr) { if (data.trmat.has_value()) {
assert(tr == nullptr);
tr = &(*data.trmat);
}
if (tr != nullptr) {
volume->set_transformation(*tr); volume->set_transformation(*tr);
} else { } else {
// apply fix matrix made by store to .3mf // apply fix matrix made by store to .3mf
@ -1053,7 +1053,6 @@ void update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3
if (emboss_shape.has_value() && emboss_shape->fix_3mf_tr.has_value()) if (emboss_shape.has_value() && emboss_shape->fix_3mf_tr.has_value())
volume->set_transformation(volume->get_matrix() * emboss_shape->fix_3mf_tr->inverse()); volume->set_transformation(volume->get_matrix() * emboss_shape->fix_3mf_tr->inverse());
} }
UpdateJob::update_volume(volume, std::move(mesh), *data.base); UpdateJob::update_volume(volume, std::move(mesh), *data.base);
} }
@ -1299,10 +1298,6 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
assert(get_count_lines(es.shapes_with_ids) == input1.text_lines.size()); assert(get_count_lines(es.shapes_with_ids) == input1.text_lines.size());
size_t count_lines = 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); 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 = 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) size_t s_i_offset = 0; // shape index offset(for next lines)
indexed_triangle_set result; indexed_triangle_set result;
@ -1310,7 +1305,7 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
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, es.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(line_bbs, samples, line.polygon);
for (size_t i = 0; i < line_bbs.size(); ++i) { for (size_t i = 0; i < line_bbs.size(); ++i) {
const BoundingBox &glyph_bb = line_bbs[i]; const BoundingBox &glyph_bb = line_bbs[i];
@ -1493,11 +1488,11 @@ const GLVolume *find_closest(
return closest; return closest;
} }
bool start_create_object_job(const CreateVolumeParams &input, DataBasePtr emboss_data, const Vec2d &coor) bool start_create_object_job(CreateVolumeParams &input, const Vec2d &coor)
{ {
const Pointfs &bed_shape = input.build_volume.bed_shape(); const Pointfs &bed_shape = input.build_volume.bed_shape();
auto gizmo_type = static_cast<GLGizmosManager::EType>(input.gizmo); auto gizmo_type = static_cast<GLGizmosManager::EType>(input.gizmo);
DataCreateObject data{std::move(emboss_data), coor, input.camera, bed_shape, gizmo_type, input.angle}; DataCreateObject data{std::move(input.data), coor, input.camera, bed_shape, gizmo_type, input.angle};
// Fix: adding text on print bed with style containing use_surface // Fix: adding text on print bed with style containing use_surface
if (data.base->shape.projection.use_surface) if (data.base->shape.projection.use_surface)
@ -1508,53 +1503,70 @@ bool start_create_object_job(const CreateVolumeParams &input, DataBasePtr emboss
return queue_job(input.worker, std::move(job)); return queue_job(input.worker, std::move(job));
} }
bool start_create_volume_on_surface_job(CreateVolumeParams &input, DataBasePtr data, const Vec2d &screen_coor, bool try_no_coor) namespace {
// for creation volume
ModelVolumePtrs prepare_volumes_to_slice(const ModelObject &mo) {
const ModelVolumePtrs &volumes = mo.volumes;
ModelVolumePtrs result;
result.reserve(volumes.size());
for (ModelVolume *volume : volumes) {
// only part could be surface for volumes
if (!volume->is_model_part())
continue;
result.push_back(volume);
}
return result;
}
} // namespace
bool start_create_volume_on_surface_job(CreateVolumeParams &input, const Vec2d &screen_coor, bool try_no_coor)
{ {
auto on_bad_state = [&input, try_no_coor](DataBasePtr data_, const ModelObject *object = nullptr) { auto on_bad_state = [&input, try_no_coor](const ModelObject *object = nullptr) {
if (try_no_coor) { if (try_no_coor) {
// Can't create on coordinate try to create somewhere // Can't create on coordinate try to create somewhere
return start_create_volume_without_position(input, std::move(data_)); return start_create_volume_without_position(input);
} else { } else {
// In centroid of convex hull is not hit with object. e.g. torid // In centroid of convex hull is not hit with object. e.g. torid
// soo create transfomation on border of object // soo create transfomation on border of object
// there is no point on surface so no use of surface will be applied // there is no point on surface so no use of surface will be applied
if (data_->shape.projection.use_surface) if (input.data->shape.projection.use_surface)
data_->shape.projection.use_surface = false; input.data->shape.projection.use_surface = false;
if (object == nullptr) if (object == nullptr)
return false; return false;
auto gizmo_type = static_cast<GLGizmosManager::EType>(input.gizmo); auto gizmo_type = static_cast<GLGizmosManager::EType>(input.gizmo);
return start_create_volume_job(input.worker, *object, {}, std::move(data_), input.volume_type, gizmo_type); return start_create_volume_job(input.worker, *object, {}, std::move(input.data), input.volume_type, gizmo_type);
} }
}; };
assert(input.gl_volume != nullptr); assert(input.gl_volume != nullptr);
if (input.gl_volume == nullptr) if (input.gl_volume == nullptr)
return on_bad_state(std::move(data)); return on_bad_state();
const Model *model = input.canvas.get_model(); const Model *model = input.canvas.get_model();
assert(model != nullptr); assert(model != nullptr);
if (model == nullptr) if (model == nullptr)
return on_bad_state(std::move(data)); return on_bad_state();
const ModelObjectPtrs &objects = model->objects; const ModelObjectPtrs &objects = model->objects;
const ModelVolume *volume = get_model_volume(*input.gl_volume, objects); const ModelVolume *volume = get_model_volume(*input.gl_volume, objects);
assert(volume != nullptr); assert(volume != nullptr);
if (volume == nullptr) if (volume == nullptr)
return on_bad_state(std::move(data)); return on_bad_state();
const ModelInstance *instance = get_model_instance(*input.gl_volume, objects); const ModelInstance *instance = get_model_instance(*input.gl_volume, objects);
assert(instance != nullptr); assert(instance != nullptr);
if (instance == nullptr) if (instance == nullptr)
return on_bad_state(std::move(data)); return on_bad_state();
const ModelObject *object = volume->get_object(); const ModelObject *object = volume->get_object();
assert(object != nullptr); assert(object != nullptr);
if (object == nullptr) if (object == nullptr)
return on_bad_state(std::move(data)); return on_bad_state();
auto cond = RaycastManager::AllowVolumes({volume->id().id}); auto cond = RaycastManager::AllowVolumes({volume->id().id});
RaycastManager::Meshes meshes = create_meshes(input.canvas, cond); RaycastManager::Meshes meshes = create_meshes(input.canvas, cond);
@ -1567,15 +1579,19 @@ bool start_create_volume_on_surface_job(CreateVolumeParams &input, DataBasePtr d
if (!hit.has_value()) if (!hit.has_value())
// When model is broken. It could appear that hit miss the object. // When model is broken. It could appear that hit miss the object.
// So add part near by in simmilar manner as right panel do // So add part near by in simmilar manner as right panel do
return on_bad_state(std::move(data), object); return on_bad_state(object);
// Create result volume transformation // Create result volume transformation
Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, UP_LIMIT); Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, UP_LIMIT);
apply_transformation(input.angle, input.distance, surface_trmat); apply_transformation(input.angle, input.distance, surface_trmat);
Transform3d transform = instance->get_matrix().inverse() * surface_trmat; Transform3d transform = instance->get_matrix().inverse() * surface_trmat;
auto gizmo_type = static_cast<GLGizmosManager::EType>(input.gizmo); auto gizmo_type = static_cast<GLGizmosManager::EType>(input.gizmo);
// Create text lines for Per Glyph projection when needed
input.data->create_text_lines(transform, prepare_volumes_to_slice(*object));
// Try to cast ray into scene and find object for add volume // Try to cast ray into scene and find object for add volume
return start_create_volume_job(input.worker, *object, transform, std::move(data), input.volume_type, gizmo_type); return start_create_volume_job(input.worker, *object, transform, std::move(input.data), input.volume_type, gizmo_type);
} }
void create_message(const std::string &message) { void create_message(const std::string &message) {

View File

@ -65,6 +65,14 @@ public:
// False (engraved).. move into object (NEGATIVE_VOLUME) // False (engraved).. move into object (NEGATIVE_VOLUME)
bool is_outside = true; bool is_outside = true;
/// <summary>
/// Used only with text for embossing per glyph
/// </summary>
/// <param name="tr">Embossed volume final transformation in world</param>
/// <param name="vols">Volumes to be sliced to text lines</param>
/// <returns>True on succes otherwise False(Per glyph shoud be disabled)</returns>
virtual bool create_text_lines(const Transform3d& tr, const ModelVolumePtrs &vols) { return false; }
// Define per letter projection on one text line // Define per letter projection on one text line
// [optional] It is not used when empty // [optional] It is not used when empty
Slic3r::Emboss::TextLines text_lines = {}; Slic3r::Emboss::TextLines text_lines = {};
@ -116,6 +124,10 @@ struct DataUpdate
// Used for prevent flooding Undo/Redo stack on slider. // Used for prevent flooding Undo/Redo stack on slider.
bool make_snapshot; bool make_snapshot;
// Transformation of volume after update volume shape
// NOTE: Add for style change, because it change rotation and distance from surface
std::optional<Transform3d> trmat;
}; };
/// <summary> /// <summary>
@ -203,6 +215,10 @@ SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume &volume)
/// </summary> /// </summary>
struct CreateVolumeParams struct CreateVolumeParams
{ {
// base input data for job
// When nullptr there is some issue with creation params ...
DataBasePtr data;
GLCanvas3D &canvas; GLCanvas3D &canvas;
// Direction of ray into scene // Direction of ray into scene
@ -236,22 +252,15 @@ struct CreateVolumeParams
/// <summary> /// <summary>
/// Create new volume on position of mouse cursor /// Create new volume on position of mouse cursor
/// </summary> /// </summary>
/// <param name="plater_ptr">canvas + camera + bed shape + </param> /// <param name="input">Cantain all needed data for start creation job</param>
/// <param name="data">Shape of emboss</param>
/// <param name="volume_type">New created volume type</param>
/// <param name="raycaster">Knows object in scene</param>
/// <param name="gizmo">Define which gizmo open on the success - enum GLGizmosManager::EType</param>
/// <param name="mouse_pos">Define position where to create volume</param>
/// <param name="distance">Wanted additionl move in Z(emboss) direction of new created volume</param>
/// <param name="angle">Wanted additionl rotation around Z of new created volume</param>
/// <returns>True on success otherwise False</returns> /// <returns>True on success otherwise False</returns>
bool start_create_volume(CreateVolumeParams &input, DataBasePtr data, const Vec2d &mouse_pos); bool start_create_volume(CreateVolumeParams &input, const Vec2d &mouse_pos);
/// <summary> /// <summary>
/// Same as previous function but without mouse position /// Same as previous function but without mouse position
/// Need to suggest position or put near the selection /// Need to suggest position or put near the selection
/// </summary> /// </summary>
bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr data); bool start_create_volume_without_position(CreateVolumeParams &input);
/// <summary> /// <summary>
/// Start job for update embossed volume /// Start job for update embossed volume

View File

@ -25,6 +25,14 @@ using namespace Slic3r::Emboss;
using namespace Slic3r::GUI; using namespace Slic3r::GUI;
namespace { namespace {
// Used to move slice (text line) on place where is approx vertical center of text
// When copy value const double ASCENT_CENTER from Emboss.cpp and Vertical align is center than
// text line will cross object center
const double ascent_ratio_offset = 1 / 3.;
double calc_line_height_in_mm(const Slic3r::Emboss::FontFile& ff, const FontProp& fp); // return lineheight in mm
// Be careful it is not water tide and contain self intersections // Be careful it is not water tide and contain self intersections
// It is only for visualization purposes // It is only for visualization purposes
indexed_triangle_set its_create_torus(const Slic3r::Polygon &polygon, float radius, size_t steps = 20) indexed_triangle_set its_create_torus(const Slic3r::Polygon &polygon, float radius, size_t steps = 20)
@ -238,63 +246,13 @@ void TextLinesModel::init(const Transform3d &text_tr,
const FontFile &ff = *ff_ptr; const FontFile &ff = *ff_ptr;
const FontProp &fp = style_manager.get_font_prop(); const FontProp &fp = style_manager.get_font_prop();
FontProp::VerticalAlign align = fp.align.second;
double line_height_mm = calc_line_height_in_mm(ff, fp);
assert(line_height_mm > 0);
if (line_height_mm <= 0)
return;
m_model.reset(); m_model.reset();
m_lines.clear();
// size_in_mm .. contain volume scale and should be ascent value in mm double line_height_mm;
double line_offset = fp.size_in_mm * ascent_ratio_offset; m_lines = Slic3r::Emboss::create_text_lines(
double first_line_center = line_offset + get_align_y_offset_in_mm(align, count_lines, ff, fp); text_tr, volumes_to_slice, ff, fp, count_lines, &line_height_mm);
std::vector<float> line_centers(count_lines); if (m_lines.empty())
for (size_t i = 0; i < count_lines; ++i) return;
line_centers[i] = static_cast<float>(first_line_center - i * line_height_mm);
// contour transformation
Transform3d c_trafo = text_tr * get_rotation();
Transform3d c_trafo_inv = c_trafo.inverse();
std::vector<Polygons> line_contours(count_lines);
for (const ModelVolume *volume : volumes_to_slice) {
MeshSlicingParams slicing_params;
slicing_params.trafo = c_trafo_inv * volume->get_matrix();
for (size_t i = 0; i < count_lines; ++i) {
const Polygons polys = Slic3r::slice_mesh(volume->mesh().its, line_centers[i], slicing_params);
if (polys.empty())
continue;
Polygons &contours = line_contours[i];
contours.insert(contours.end(), polys.begin(), polys.end());
}
}
// fix for text line out of object
// When move text close to edge - line center could be out of object
for (Polygons &contours: line_contours) {
if (!contours.empty())
continue;
// use line center at zero, there should be some contour.
float line_center = 0.f;
for (const ModelVolume *volume : volumes_to_slice) {
MeshSlicingParams slicing_params;
slicing_params.trafo = c_trafo_inv * volume->get_matrix();
const Polygons polys = Slic3r::slice_mesh(volume->mesh().its, line_center, slicing_params);
if (polys.empty())
continue;
contours.insert(contours.end(), polys.begin(), polys.end());
}
}
m_lines = select_closest_contour(line_contours);
assert(m_lines.size() == count_lines);
assert(line_centers.size() == count_lines);
for (size_t i = 0; i < count_lines; ++i)
m_lines[i].y = line_centers[i];
bool is_mirrored = has_reflection(text_tr); bool is_mirrored = has_reflection(text_tr);
float radius = static_cast<float>(line_height_mm / 20.); float radius = static_cast<float>(line_height_mm / 20.);
@ -346,9 +304,82 @@ void TextLinesModel::render(const Transform3d &text_world)
shader->stop_using(); shader->stop_using();
} }
double TextLinesModel::calc_line_height_in_mm(const Slic3r::Emboss::FontFile &ff, const FontProp &fp) namespace {
{ double calc_line_height_in_mm(const Slic3r::Emboss::FontFile &ff, const FontProp &fp) {
int line_height = Slic3r::Emboss::get_line_height(ff, fp); // In shape size int line_height = Slic3r::Emboss::get_line_height(ff, fp); // In shape size
double scale = Slic3r::Emboss::get_text_shape_scale(fp, ff); double scale = Slic3r::Emboss::get_text_shape_scale(fp, ff);
return line_height * scale; return line_height * scale;
} }
} // namespace
Slic3r::Emboss::TextLines Slic3r::Emboss::create_text_lines(
const Transform3d &text_tr,
const ModelVolumePtrs &volumes_to_slice,
const FontFile &ff,
const FontProp &fp,
unsigned count_lines,
double *line_height_mm_ptr
) {
FontProp::VerticalAlign align = fp.align.second;
double line_height_mm = calc_line_height_in_mm(ff, fp);
assert(line_height_mm > 0);
if (line_height_mm <= 0)
return {};
// size_in_mm .. contain volume scale and should be ascent value in mm
double line_offset = fp.size_in_mm * ascent_ratio_offset;
double first_line_center = line_offset + get_align_y_offset_in_mm(align, count_lines, ff, fp);
std::vector<float> line_centers(count_lines);
for (size_t i = 0; i < count_lines; ++i)
line_centers[i] = static_cast<float>(first_line_center - i * line_height_mm);
// contour transformation
Transform3d c_trafo = text_tr * get_rotation();
Transform3d c_trafo_inv = c_trafo.inverse();
std::vector<Polygons> line_contours(count_lines);
for (const ModelVolume *volume : volumes_to_slice) {
MeshSlicingParams slicing_params;
slicing_params.trafo = c_trafo_inv * volume->get_matrix();
for (size_t i = 0; i < count_lines; ++i) {
const Polygons polys =
Slic3r::slice_mesh(volume->mesh().its, line_centers[i], slicing_params);
if (polys.empty())
continue;
Polygons &contours = line_contours[i];
contours.insert(contours.end(), polys.begin(), polys.end());
}
}
// fix for text line out of object
// When move text close to edge - line center could be out of object
for (Polygons &contours : line_contours) {
if (!contours.empty())
continue;
// use line center at zero, there should be some contour.
float line_center = 0.f;
for (const ModelVolume *volume : volumes_to_slice) {
MeshSlicingParams slicing_params;
slicing_params.trafo = c_trafo_inv * volume->get_matrix();
const Polygons polys =
Slic3r::slice_mesh(volume->mesh().its, line_center, slicing_params);
if (polys.empty())
continue;
contours.insert(contours.end(), polys.begin(), polys.end());
}
}
TextLines result = select_closest_contour(line_contours);
assert(result.size() == count_lines);
assert(line_centers.size() == count_lines);
// Fill centers
for (size_t i = 0; i < count_lines; ++i)
result[i].y = line_centers[i];
if (line_height_mm_ptr != nullptr)
*line_height_mm_ptr = line_height_mm;
return result;
}

View File

@ -11,6 +11,7 @@
namespace Slic3r { namespace Slic3r {
class ModelVolume; class ModelVolume;
typedef std::vector<ModelVolume *> ModelVolumePtrs; typedef std::vector<ModelVolume *> ModelVolumePtrs;
struct FontProp;
} }
namespace Slic3r::GUI { namespace Slic3r::GUI {
@ -32,18 +33,30 @@ public:
void reset() { m_model.reset(); m_lines.clear(); } void reset() { m_model.reset(); m_lines.clear(); }
const Slic3r::Emboss::TextLines &get_lines() const { return m_lines; } const Slic3r::Emboss::TextLines &get_lines() const { return m_lines; }
static double calc_line_height_in_mm(const Slic3r::Emboss::FontFile& ff, const FontProp& fp); // return lineheight in mm
private: private:
Slic3r::Emboss::TextLines m_lines; Slic3r::Emboss::TextLines m_lines;
// Keep model for visualization text lines // Keep model for visualization text lines
GLModel m_model; GLModel m_model;
// Used to move slice (text line) on place where is approx vertical center of text
// When copy value const double ASCENT_CENTER from Emboss.cpp and Vertical align is center than
// text line will cross object center
const double ascent_ratio_offset = 1/3.;
}; };
} // namespace Slic3r::GUI } // namespace Slic3r::GUI
namespace Slic3r::Emboss{
/// <summary>
/// creation line without model for backend only
/// </summary>
/// <param name="text_tr">Transformation of text volume inside object (aka inside of
/// instance)</param> <param name="volumes_to_slice">Vector of volumes to be sliced</param> <param
/// name="ff"></param> <param name="fp"></param> <param name="count_lines">Count lines of embossed
/// text(for veritcal alignment)</param> <param name="line_height_mm_ptr">[output] line height in
/// mm</param> <returns></returns>
TextLines create_text_lines(
const Transform3d &text_tr,
const ModelVolumePtrs &volumes_to_slice,
const FontFile &ff,
const FontProp &fp,
unsigned count_lines = 1,
double *line_height_mm_ptr = nullptr
);
}
#endif // slic3r_TextLines_hpp_ #endif // slic3r_TextLines_hpp_