Fix embossing text PerGlyph stored in style (initialize per glyph on backend)

Separate draw of advanced input.
Fix generate of preview rotation(with "per glyph" or "use surface")
Fix per glyph orientation is calculated from glyph width.
(not fixed 5mm - better result on edges)
Add Cancel job before check that value is changed.
Remove "FontPoint" units from GUI (use mm or in)
Change range for boldness values
This commit is contained in:
Filip Sykala - NTB T15p 2024-01-23 11:11:19 +01:00 committed by Lukas Matena
parent 352e71e5a8
commit 108e201ec7
10 changed files with 788 additions and 517 deletions

View File

@ -776,13 +776,12 @@ const Glyph* get_glyph(
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;
@ -1068,9 +1067,8 @@ std::unique_ptr<FontFile> Emboss::create_font_file(
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));
} }
@ -1887,12 +1885,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());
assert(glyph_sizes.size() == polygon_points.size());
if (glyph_sizes.size() != polygon_points.size()) {
// only backup solution should not be used
for (const PolygonPoint &pp : polygon_points) for (const PolygonPoint &pp : polygon_points)
result.emplace_back(calculate_angle(distance, pp, polygon)); 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>
@ -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

@ -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];
@ -1300,17 +1295,13 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
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;
for (size_t text_line_index = 0; text_line_index < input1.text_lines.size(); ++text_line_index) { for (size_t text_line_index = 0; text_line_index < input1.text_lines.size(); ++text_line_index) {
const BoundingBoxes &line_bbs = bbs[text_line_index]; const BoundingBoxes &line_bbs = bbs[text_line_index];
const TextLine &line = input1.text_lines[text_line_index]; const TextLine &line = input1.text_lines[text_line_index];
PolygonPoints samples = sample_slice(line, line_bbs, 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 +1484,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 +1499,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 +1575,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 = {};
@ -203,6 +211,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 +248,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_