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);
if (!is_valid(font, font_index)) return nullptr;
if (!font_info_opt.has_value()) {
font_info_opt = load_font_info(font.data->data(), font_index);
if (!font_info_opt.has_value()) {
font_info_opt = load_font_info(font.data->data(), font_index);
// can load font info?
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
if (flatness < RESOLUTION) flatness = RESOLUTION;
@ -1066,11 +1065,10 @@ std::unique_ptr<FontFile> Emboss::create_font_file(
int ascent, descent, linegap;
stbtt_GetFontVMetrics(info, &ascent, &descent, &linegap);
float pixels = 1000.; // value is irelevant
float em_pixels = stbtt_ScaleForMappingEmToPixels(info, pixels);
int units_per_em = static_cast<int>(std::round(pixels / em_pixels));
infos.emplace_back(FontFile::Info{ascent, descent, linegap, units_per_em});
float pixels = 1000.; // value is irelevant
float em_pixels = stbtt_ScaleForMappingEmToPixels(info, pixels);
int unit_per_em = static_cast<int>(std::round(pixels / em_pixels));
infos.emplace_back(FontFile::Info{ascent, descent, linegap, unit_per_em});
}
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 {
ExPolygons letter2shapes(
wchar_t letter, Point &cursor, FontFileWithCache &font_with_cache, const FontProp &font_prop, fontinfo_opt& font_info_cache)
{
assert(font_with_cache.has_value());
if (!font_with_cache.has_value())
return {};
Glyphs &cache = *font_with_cache.cache;
const FontFile &font = *font_with_cache.font_file;
wchar_t letter,
Point &cursor,
const FontFile &font,
Glyphs &cache,
const FontProp &font_prop,
fontinfo_opt &font_info_cache
) {
if (letter == '\n') {
cursor.x() = 0;
// 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){
assert(font_with_cache.has_value());
if (!font_with_cache.has_value())
return {};
const FontFile &font = *font_with_cache.font_file;
unsigned int font_index = font_prop.collection_number.value_or(0);
if (!is_valid(font, font_index))
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);
fontinfo_opt font_info_cache;
@ -1340,7 +1340,7 @@ ExPolygonsWithIds Emboss::text2vshapes(FontFileWithCache &font_with_cache, const
return {};
}
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);
@ -1887,12 +1887,27 @@ double Emboss::calculate_angle(int32_t distance, PolygonPoint polygon_point, con
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;
result.reserve(polygon_points.size());
for(const PolygonPoint& pp: polygon_points)
result.emplace_back(calculate_angle(distance, pp, polygon));
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)
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;
}

View File

@ -282,8 +282,6 @@ namespace Emboss
class IProjection : public IProject3d
{
public:
virtual ~IProjection() = default;
/// <summary>
/// convert 2d point to 3d points
/// </summary>
@ -461,7 +459,11 @@ namespace Emboss
/// <param name="polygon">Polygon know neighbor of point</param>
/// <returns>angle(atan2) of normal in polygon point</returns>
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

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,9 @@ namespace Slic3r{
class AppConfig;
class GLVolume;
enum class ModelVolumeType : int;
namespace GUI::Emboss {
struct CreateVolumeParams;
}
}
namespace Slic3r::GUI {
@ -64,7 +67,6 @@ public:
/// <returns>True on success start job otherwise False</returns>
bool do_mirror(size_t axis);
/// <summary>
/// Call on change inside of object conatining projected volume
/// </summary>
@ -114,7 +116,7 @@ private:
void reset_volume();
// 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 draw_window();
void draw_text_input();
@ -139,6 +141,17 @@ private:
bool draw_bold_button();
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);
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,
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_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,
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,
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,
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 remove_notification_not_valid_font();
// initialize data for create volume in job
Emboss::CreateVolumeParams create_input(ModelVolumeType volume_type);
struct GuiCfg;
std::unique_ptr<const GuiCfg> m_gui_cfg;
@ -234,6 +248,7 @@ private:
// For text on scaled objects
std::optional<float> m_scale_height;
std::optional<float> m_scale_depth;
std::optional<float> m_scale_width;
void calculate_scale();
// drawing icons

View File

@ -118,15 +118,6 @@ std::string get_file_name(const std::string &file_path);
/// <returns>Name for volume</returns>
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 {
reset_value,
reset_value_hover,
@ -198,33 +189,38 @@ struct GLGizmoSVG::GuiCfg: public ::GuiCfg{};
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos)
{
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type);
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type);
if (!base) return false; // Uninterpretable svg
return start_create_volume(input, std::move(base), mouse_pos);
CreateVolumeParams input = create_input(volume_type);
if (!input.data) return false; // Uninterpretable svg
return start_create_volume(input, mouse_pos);
}
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type)
{
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type);
DataBasePtr base = create_emboss_data_base(m_job_cancel,volume_type);
if (!base) return false; // Uninterpretable svg
return start_create_volume_without_position(input, std::move(base));
CreateVolumeParams input = create_input(volume_type);
if (!input.data) return false; // Uninterpretable svg
return start_create_volume_without_position(input);
}
bool GLGizmoSVG::create_volume(std::string_view svg_file, ModelVolumeType volume_type){
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type);
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type, svg_file);
if (!base) return false; // Uninterpretable svg
return start_create_volume_without_position(input, std::move(base));
CreateVolumeParams input = create_input(volume_type, svg_file);
if (!input.data) return false; // Uninterpretable svg
return start_create_volume_without_position(input);
}
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);
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type, svg_file);
if (!base) return false; // Uninterpretable svg
return start_create_volume(input, std::move(base), mouse_pos);
CreateVolumeParams input = create_input(volume_type, svg_file);
if (!input.data) return false; // Uninterpretable svg
return start_create_volume(input, 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) {
@ -2117,15 +2113,6 @@ std::string volume_name(const EmbossShape &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 cfg; // initialize by default values;

View File

@ -24,6 +24,9 @@
namespace Slic3r{
class ModelVolume;
enum class ModelVolumeType : int;
namespace GUI::Emboss {
struct CreateVolumeParams;
}
}
namespace Slic3r::GUI {
@ -133,6 +136,8 @@ private:
void volume_transformation_changed();
Emboss::CreateVolumeParams create_input(ModelVolumeType volume_type, std::string_view svg_filepath = "");
struct GuiCfg;
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="data">Text configuration, ...</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>
/// 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))
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)
@ -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
// 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 {
@ -538,11 +538,11 @@ const GLVolume *find_closest(
/// <summary>
/// Start job for add object with text into scene
/// </summary>
/// <param name="input">Contain worker, build shape, gizmo</param>
/// <param name="emboss_data">Define params for create volume</param>
/// <param name="input">Contain worker, build shape, gizmo,
/// 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>
/// <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>
/// 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,
/// False .. </param>
/// <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
@ -568,25 +568,25 @@ SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume &text_vo
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;
if (!check(input))
return false;
if (input.gl_volume == nullptr)
// 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;
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);
if (data == nullptr)
assert(input.data != nullptr);
if (input.data == nullptr)
return false;
if (!check(input))
return false;
@ -604,17 +604,17 @@ bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr
static_cast<size_t>(object_idx) >= objects.size())
// create Object on center of screen
// 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
Vec2d coor;
const Camera &camera = wxGetApp().plater()->get_camera();
input.gl_volume = ::find_closest(selection, screen_center, camera, objects, &coor);
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;
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
@ -850,18 +850,13 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
double depth = shape.projection.depth / 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)
indexed_triangle_set result;
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);
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) {
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);
}
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
if (mesh.its.empty())
@ -1044,7 +1039,12 @@ void update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3
if (volume == nullptr)
return;
if (tr) {
if (data.trmat.has_value()) {
assert(tr == nullptr);
tr = &(*data.trmat);
}
if (tr != nullptr) {
volume->set_transformation(*tr);
} else {
// 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())
volume->set_transformation(volume->get_matrix() * emboss_shape->fix_3mf_tr->inverse());
}
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());
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 = 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)
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 TextLine &line = input1.text_lines[text_line_index];
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) {
const BoundingBox &glyph_bb = line_bbs[i];
@ -1493,11 +1488,11 @@ const GLVolume *find_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();
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
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));
}
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) {
// 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 {
// In centroid of convex hull is not hit with object. e.g. torid
// soo create transfomation on border of object
// there is no point on surface so no use of surface will be applied
if (data_->shape.projection.use_surface)
data_->shape.projection.use_surface = false;
if (input.data->shape.projection.use_surface)
input.data->shape.projection.use_surface = false;
if (object == nullptr)
return false;
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);
if (input.gl_volume == nullptr)
return on_bad_state(std::move(data));
return on_bad_state();
const Model *model = input.canvas.get_model();
assert(model != nullptr);
if (model == nullptr)
return on_bad_state(std::move(data));
return on_bad_state();
const ModelObjectPtrs &objects = model->objects;
const ModelVolume *volume = get_model_volume(*input.gl_volume, objects);
assert(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);
assert(instance != nullptr);
if (instance == nullptr)
return on_bad_state(std::move(data));
return on_bad_state();
const ModelObject *object = volume->get_object();
assert(object != nullptr);
if (object == nullptr)
return on_bad_state(std::move(data));
return on_bad_state();
auto cond = RaycastManager::AllowVolumes({volume->id().id});
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())
// When model is broken. It could appear that hit miss the object.
// 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
Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, UP_LIMIT);
apply_transformation(input.angle, input.distance, 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
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) {

View File

@ -65,6 +65,14 @@ public:
// False (engraved).. move into object (NEGATIVE_VOLUME)
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
// [optional] It is not used when empty
Slic3r::Emboss::TextLines text_lines = {};
@ -116,6 +124,10 @@ struct DataUpdate
// Used for prevent flooding Undo/Redo stack on slider.
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>
@ -203,6 +215,10 @@ SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume &volume)
/// </summary>
struct CreateVolumeParams
{
// base input data for job
// When nullptr there is some issue with creation params ...
DataBasePtr data;
GLCanvas3D &canvas;
// Direction of ray into scene
@ -236,22 +252,15 @@ struct CreateVolumeParams
/// <summary>
/// Create new volume on position of mouse cursor
/// </summary>
/// <param name="plater_ptr">canvas + camera + bed shape + </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>
/// <param name="input">Cantain all needed data for start creation job</param>
/// <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>
/// Same as previous function but without mouse position
/// Need to suggest position or put near the selection
/// </summary>
bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr data);
bool start_create_volume_without_position(CreateVolumeParams &input);
/// <summary>
/// Start job for update embossed volume

View File

@ -25,6 +25,14 @@ using namespace Slic3r::Emboss;
using namespace Slic3r::GUI;
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
// It is only for visualization purposes
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 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_lines.clear();
// 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());
}
}
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];
double line_height_mm;
m_lines = Slic3r::Emboss::create_text_lines(
text_tr, volumes_to_slice, ff, fp, count_lines, &line_height_mm);
if (m_lines.empty())
return;
bool is_mirrored = has_reflection(text_tr);
float radius = static_cast<float>(line_height_mm / 20.);
@ -346,9 +304,82 @@ void TextLinesModel::render(const Transform3d &text_world)
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
double scale = Slic3r::Emboss::get_text_shape_scale(fp, ff);
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 {
class ModelVolume;
typedef std::vector<ModelVolume *> ModelVolumePtrs;
struct FontProp;
}
namespace Slic3r::GUI {
@ -32,18 +33,30 @@ public:
void reset() { m_model.reset(); m_lines.clear(); }
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:
Slic3r::Emboss::TextLines m_lines;
// Keep model for visualization text lines
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::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_