add option to Create per glyph volume (initialize text lines before text volume)

Per glyph for negative volume and modifiers are moved into object
This commit is contained in:
Filip Sykala - NTB T15p 2023-05-25 13:01:30 +02:00
parent 496cc54bd7
commit d2a605ee2e
8 changed files with 222 additions and 111 deletions

View File

@ -148,12 +148,14 @@ namespace priv {
/// <param name="style_manager">Keep actual selected style</param> /// <param name="style_manager">Keep actual selected style</param>
/// <param name="text_lines">Needed when transform per glyph</param> /// <param name="text_lines">Needed when transform per glyph</param>
/// <param name="selection">Needed for transform per glyph</param> /// <param name="selection">Needed for transform per glyph</param>
/// <param name="type">Define type of volume - side of surface(in / out)</param>
/// <param name="cancel">Cancel for previous job</param> /// <param name="cancel">Cancel for previous job</param>
/// <returns>Base data for emboss text</returns> /// <returns>Base data for emboss text</returns>
static DataBase create_emboss_data_base(const std::string &text, static DataBase create_emboss_data_base(const std::string &text,
StyleManager &style_manager, StyleManager &style_manager,
TextLinesModel &text_lines, TextLinesModel &text_lines,
const Selection &selection, const Selection &selection,
ModelVolumeType type,
std::shared_ptr<std::atomic<bool>> &cancel); std::shared_ptr<std::atomic<bool>> &cancel);
/// <summary> /// <summary>
@ -176,6 +178,8 @@ static void start_create_volume_job(const ModelObject *object,
/// <param name="screen_coor">Mouse position which define position</param> /// <param name="screen_coor">Mouse position which define position</param>
/// <param name="gl_volume">Volume to find surface for create</param> /// <param name="gl_volume">Volume to find surface for create</param>
/// <param name="raycaster">Ability to ray cast to model</param> /// <param name="raycaster">Ability to ray cast to model</param>
/// <param name="text_lines">Per glyph transformation</param>
/// <param name="style_manager">Line height need font file/param>
/// <param name="canvas">Contain already used scene RayCasters</param> /// <param name="canvas">Contain already used scene RayCasters</param>
/// <returns>True when start creation, False when there is no hit surface by screen coor</returns> /// <returns>True when start creation, False when there is no hit surface by screen coor</returns>
static bool start_create_volume_on_surface_job(DataBase &emboss_data, static bool start_create_volume_on_surface_job(DataBase &emboss_data,
@ -183,6 +187,8 @@ static bool start_create_volume_on_surface_job(DataBase &emboss_data,
const Vec2d &screen_coor, const Vec2d &screen_coor,
const GLVolume *gl_volume, const GLVolume *gl_volume,
RaycastManager &raycaster, RaycastManager &raycaster,
TextLinesModel &text_lines,
/*const */ StyleManager &style_manager,
GLCanvas3D &canvas); GLCanvas3D &canvas);
/// <summary> /// <summary>
@ -249,7 +255,29 @@ static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas, bool keep
} // namespace priv } // namespace priv
namespace { namespace {
// for existing volume which is selected(could init different(to volume text) lines count when edit text)
void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines=0); void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines=0);
// before text volume is created
void init_new_text_line(TextLinesModel &text_lines, const Transform3d& new_text_tr, const ModelObject& mo, /* const*/ StyleManager &style_manager)
{
// prepare volumes to slice
ModelVolumePtrs volumes;
volumes.reserve(mo.volumes.size());
for (ModelVolume *volume : mo.volumes) {
// only part could be surface for volumes
if (!volume->is_model_part())
continue;
volumes.push_back(volume);
}
double line_height = style_manager.get_line_height();
if (line_height < 0)
return;
FontProp::Align align = style_manager.get_font_prop().align;
unsigned count_lines = 1;
text_lines.init(new_text_tr, volumes, align, line_height, count_lines);
}
} }
void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
@ -258,11 +286,11 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
return; return;
const GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent); const GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent);
DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), volume_type, m_job_cancel);
bool is_simple_mode = wxGetApp().get_mode() == comSimple; bool is_simple_mode = wxGetApp().get_mode() == comSimple;
if (gl_volume != nullptr && !is_simple_mode) { if (gl_volume != nullptr && !is_simple_mode) {
// Try to cast ray into scene and find object for add volume // Try to cast ray into scene and find object for add volume
if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager, m_parent)) { if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager, m_text_lines, m_style_manager, m_parent)) {
// 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
create_volume(volume_type); create_volume(volume_type);
@ -285,7 +313,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
Size s = m_parent.get_canvas_size(); Size s = m_parent.get_canvas_size();
Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.); Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.);
DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), volume_type, m_job_cancel);
const ModelObjectPtrs &objects = selection.get_model()->objects; const ModelObjectPtrs &objects = selection.get_model()->objects;
bool is_simple_mode = wxGetApp().get_mode() == comSimple; bool is_simple_mode = wxGetApp().get_mode() == comSimple;
// No selected object so create new object // No selected object so create new object
@ -303,7 +331,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol); priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol);
if (vol == nullptr) { if (vol == nullptr) {
priv::start_create_object_job(emboss_data, screen_center); priv::start_create_object_job(emboss_data, screen_center);
} else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager, m_parent)) { } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager, m_text_lines, m_style_manager, m_parent)) {
// in centroid of convex hull is not hit with object // in centroid of convex hull is not hit with object
// soo create transfomation on border of object // soo create transfomation on border of object
@ -321,7 +349,11 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
- instance_bb.size().y() / 2 - prop.size_in_mm / 2, // under - instance_bb.size().y() / 2 - prop.size_in_mm / 2, // under
prop.emboss / 2 - instance_bb.size().z() / 2 // lay on bed prop.emboss / 2 - instance_bb.size().z() / 2 // lay on bed
); );
Transform3d volume_trmat = tr * Eigen::Translation3d(offset_tr); Transform3d volume_trmat = tr * Eigen::Translation3d(offset_tr);
if (prop.per_glyph) {
init_new_text_line(m_text_lines, volume_trmat, *obj, m_style_manager);
emboss_data.text_lines = m_text_lines.get_lines();
}
priv::start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); priv::start_create_volume_job(obj, volume_trmat, emboss_data, volume_type);
} }
} }
@ -1062,26 +1094,65 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
namespace { namespace {
void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines) void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines)
{ {
assert(style_manager.is_active_font()); double line_height = style_manager.get_line_height();
if (!style_manager.is_active_font()) if (line_height < 0)
return; return;
const auto &ffc = style_manager.get_font_file_with_cache();
assert(ffc.has_value()); const GLVolume *gl_volume_ptr = selection.get_first_volume();
if (!ffc.has_value()) if (gl_volume_ptr == nullptr)
return; return;
const auto &ff_ptr = ffc.font_file; const GLVolume &gl_volume = *gl_volume_ptr;
assert(ff_ptr != nullptr); const ModelObjectPtrs &objects = selection.get_model()->objects;
if (ff_ptr == nullptr) const ModelObject *mo_ptr = get_model_object(gl_volume, objects);
if (mo_ptr == nullptr)
return;
const ModelObject &mo = *mo_ptr;
const ModelVolume *mv_ptr = get_model_volume(gl_volume, objects);
if (mv_ptr == nullptr)
return;
const ModelVolume &mv = *mv_ptr;
if (mv.is_the_only_one_part())
return; return;
const std::optional<TextConfiguration> &tc_opt = mv.text_configuration;
if (!tc_opt.has_value())
return;
const TextConfiguration &tc = *tc_opt;
// calculate count lines when not set
if (count_lines == 0) {
count_lines = get_count_lines(tc.text);
if (count_lines == 0)
return;
}
// prepare volumes to slice
ModelVolumePtrs volumes;
volumes.reserve(mo.volumes.size());
for (ModelVolume *volume : mo.volumes) {
// only part could be surface for volumes
if (!volume->is_model_part())
continue;
// is selected volume
if (mv.id() == volume->id())
continue;
volumes.push_back(volume);
}
// For interactivity during drag over surface it must be from gl_volume not volume.
const Transform3d &mv_trafo = gl_volume.get_volume_transformation().get_matrix();
const FontProp &fp = style_manager.get_font_prop(); const FontProp &fp = style_manager.get_font_prop();
const FontFile &ff = *ff_ptr; text_lines.init(mv_trafo, volumes, fp.align, line_height, count_lines);
double line_height = TextLinesModel::calc_line_height(ff, fp);
text_lines.init(selection, line_height, count_lines);
} }
} }
void GLGizmoEmboss::reinit_text_lines(unsigned count_lines) {
init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, count_lines);
}
void GLGizmoEmboss::set_volume_by_selection() void GLGizmoEmboss::set_volume_by_selection()
{ {
const Selection &selection = m_parent.get_selection(); const Selection &selection = m_parent.get_selection();
@ -1221,7 +1292,7 @@ void GLGizmoEmboss::set_volume_by_selection()
m_volume_id = volume->id(); m_volume_id = volume->id();
if (tc.style.prop.per_glyph) if (tc.style.prop.per_glyph)
init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager); reinit_text_lines();
// Calculate current angle of up vector // Calculate current angle of up vector
assert(m_style_manager.is_active_font()); assert(m_style_manager.is_active_font());
@ -1304,7 +1375,7 @@ bool GLGizmoEmboss::process()
// exist loaded font file? // exist loaded font file?
if (!m_style_manager.is_active_font()) return false; if (!m_style_manager.is_active_font()) return false;
DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel), DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_volume->type(), m_job_cancel),
m_volume->id()}; m_volume->id()};
std::unique_ptr<Job> job = nullptr; std::unique_ptr<Job> job = nullptr;
@ -1338,7 +1409,7 @@ bool GLGizmoEmboss::process()
// check that there is not unexpected volume type // check that there is not unexpected volume type
assert(is_outside || m_volume->is_negative_volume() || assert(is_outside || m_volume->is_negative_volume() ||
m_volume->is_modifier()); m_volume->is_modifier());
UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, is_outside, std::move(sources)}}; UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, std::move(sources)}};
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data)); job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
} else { } else {
job = std::make_unique<UpdateJob>(std::move(data)); job = std::make_unique<UpdateJob>(std::move(data));
@ -1555,7 +1626,7 @@ void GLGizmoEmboss::draw_text_input()
unsigned count_lines = get_count_lines(m_text); unsigned count_lines = get_count_lines(m_text);
if (count_lines != m_text_lines.get_lines().size()) if (count_lines != m_text_lines.get_lines().size())
// Necesarry to initialize count by given number (differ from stored in volume at the moment) // Necesarry to initialize count by given number (differ from stored in volume at the moment)
init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, count_lines); reinit_text_lines(count_lines);
} }
process(); process();
range_text = create_range_text_prep(); range_text = create_range_text_prep();
@ -1982,6 +2053,8 @@ void GLGizmoEmboss::draw_font_list_line()
if (exist_change) { if (exist_change) {
m_style_manager.clear_glyphs_cache(); m_style_manager.clear_glyphs_cache();
if (m_style_manager.get_font_prop().per_glyph)
reinit_text_lines(m_text_lines.get_lines().size());
process(); process();
} }
} }
@ -2206,7 +2279,8 @@ void GLGizmoEmboss::draw_model_type()
m_volume->set_type(*new_type); m_volume->set_type(*new_type);
// Update volume position when switch from part or into part // Update volume position when switch from part or into part
if (m_volume->text_configuration->style.prop.use_surface) { const FontProp& prop = m_volume->text_configuration->style.prop;
if (prop.use_surface || prop.per_glyph) {
// move inside // move inside
bool is_volume_move_inside = (type == part); bool is_volume_move_inside = (type == part);
bool is_volume_move_outside = (*new_type == part); bool is_volume_move_outside = (*new_type == part);
@ -2835,6 +2909,9 @@ bool GLGizmoEmboss::set_height() {
if (is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) if (is_approx(value, m_volume->text_configuration->style.prop.size_in_mm))
return false; return false;
if (m_style_manager.get_font_prop().per_glyph)
reinit_text_lines(m_text_lines.get_lines().size());
#ifdef USE_PIXEL_SIZE_IN_WX_FONT #ifdef USE_PIXEL_SIZE_IN_WX_FONT
// store font size into path serialization // store font size into path serialization
const wxFont &wx_font = m_style_manager.get_wx_font(); const wxFont &wx_font = m_style_manager.get_wx_font();
@ -3047,6 +3124,8 @@ void GLGizmoEmboss::draw_advanced()
const bool *def_per_glyph = stored_style ? &stored_style->prop.per_glyph : nullptr; const bool *def_per_glyph = stored_style ? &stored_style->prop.per_glyph : nullptr;
if (rev_checkbox(tr.per_glyph, per_glyph, def_per_glyph, if (rev_checkbox(tr.per_glyph, per_glyph, def_per_glyph,
_u8L("Revert Transformation per glyph."))) { _u8L("Revert Transformation per glyph."))) {
if (per_glyph && !m_text_lines.is_init())
reinit_text_lines();
process(); process();
} else if (ImGui::IsItemHovered()) { } else if (ImGui::IsItemHovered()) {
if (per_glyph) { if (per_glyph) {
@ -3054,7 +3133,7 @@ void GLGizmoEmboss::draw_advanced()
} else { } else {
ImGui::SetTooltip("%s", _u8L("Set position and orientation per Glyph.").c_str()); ImGui::SetTooltip("%s", _u8L("Set position and orientation per Glyph.").c_str());
if (!m_text_lines.is_init()) if (!m_text_lines.is_init())
init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager); reinit_text_lines();
} }
} else if (!per_glyph && m_text_lines.is_init()) } else if (!per_glyph && m_text_lines.is_init())
m_text_lines.reset(); m_text_lines.reset();
@ -3064,10 +3143,10 @@ void GLGizmoEmboss::draw_advanced()
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(m_gui_cfg->input_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width);
if (m_imgui->slider_float("##base_line_y_offset", &m_text_lines.offset, -10.f, 10.f, "%f mm")) { if (m_imgui->slider_float("##base_line_y_offset", &m_text_lines.offset, -10.f, 10.f, "%f mm")) {
init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size()); reinit_text_lines(m_text_lines.get_lines().size());
process(); process();
} else if (ImGui::IsItemHovered()) } else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Move base line (up/down) for allign letters").c_str()); ImGui::SetTooltip("TEST PURPOSE ONLY\nMove base line (up/down) for allign letters");
m_imgui->disabled_end(); // !per_glyph m_imgui->disabled_end(); // !per_glyph
int selected_align = static_cast<int>(font_prop.align); int selected_align = static_cast<int>(font_prop.align);
@ -3094,6 +3173,8 @@ void GLGizmoEmboss::draw_advanced()
}; };
if (revertible(tr.alignment, selected_align, def_align, _u8L("Revert alignment."), undo_offset, draw)){ if (revertible(tr.alignment, selected_align, def_align, _u8L("Revert alignment."), undo_offset, draw)){
font_prop.align = static_cast<FontProp::Align>(selected_align); font_prop.align = static_cast<FontProp::Align>(selected_align);
if (font_prop.per_glyph)
reinit_text_lines(m_text_lines.get_lines().size());
// TODO: move with text in finalize to not change position // TODO: move with text in finalize to not change position
process(); process();
} }
@ -3132,6 +3213,8 @@ void GLGizmoEmboss::draw_advanced()
m_volume->text_configuration->style.prop.line_gap != font_prop.line_gap) { m_volume->text_configuration->style.prop.line_gap != font_prop.line_gap) {
// line gap is planed to be stored inside of imgui font atlas // line gap is planed to be stored inside of imgui font atlas
m_style_manager.clear_imgui_font(); m_style_manager.clear_imgui_font();
if (font_prop.per_glyph)
reinit_text_lines(m_text_lines.get_lines().size());
exist_change = true; exist_change = true;
} }
} }
@ -3198,9 +3281,13 @@ void GLGizmoEmboss::draw_advanced()
} }
if (is_moved){ if (is_moved){
m_volume->text_configuration->style.prop.distance = font_prop.distance; if (font_prop.per_glyph){
float act_distance = font_prop.distance.has_value() ? *font_prop.distance : .0f; process();
do_translate(Vec3d::UnitZ() * (act_distance - prev_distance)); } else {
m_volume->text_configuration->style.prop.distance = font_prop.distance;
float act_distance = font_prop.distance.has_value() ? *font_prop.distance : .0f;
do_translate(Vec3d::UnitZ() * (act_distance - prev_distance));
}
} }
m_imgui->disabled_end(); m_imgui->disabled_end();
@ -3234,8 +3321,11 @@ void GLGizmoEmboss::draw_advanced()
if (m_style_manager.is_active_font() && gl_volume != nullptr) if (m_style_manager.is_active_font() && gl_volume != nullptr)
m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit); m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit);
if (font_prop.per_glyph)
reinit_text_lines(m_text_lines.get_lines().size());
// recalculate for surface cut // recalculate for surface cut
if (font_prop.use_surface) if (font_prop.use_surface || font_prop.per_glyph)
process(); process();
} }
@ -3283,14 +3373,19 @@ void GLGizmoEmboss::draw_advanced()
if (exist_change) { if (exist_change) {
m_style_manager.clear_glyphs_cache(); m_style_manager.clear_glyphs_cache();
if (m_style_manager.get_font_prop().per_glyph)
reinit_text_lines();
else
m_text_lines.reset();
process(); process();
} }
if (ImGui::Button(_u8L("Set text to face camera").c_str())) { if (ImGui::Button(_u8L("Set text to face camera").c_str())) {
assert(get_selected_volume(m_parent.get_selection()) == m_volume); assert(get_selected_volume(m_parent.get_selection()) == m_volume);
const Camera &cam = wxGetApp().plater()->get_camera(); const Camera &cam = wxGetApp().plater()->get_camera();
bool use_surface = m_style_manager.get_font_prop().use_surface; const FontProp &prop = m_style_manager.get_font_prop();
if (priv::apply_camera_dir(cam, m_parent, m_keep_up) && use_surface) if (priv::apply_camera_dir(cam, m_parent, m_keep_up) &&
prop.use_surface || prop.per_glyph)
process(); process();
} else if (ImGui::IsItemHovered()) { } else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", _u8L("Orient the text towards the camera.").c_str()); ImGui::SetTooltip("%s", _u8L("Orient the text towards the camera.").c_str());
@ -3565,7 +3660,12 @@ bool priv::draw_button(const IconManager::VIcons &icons, IconType type, bool dis
// priv namespace implementation // priv namespace implementation
/////////////// ///////////////
DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, TextLinesModel& text_lines, const Selection& selection, std::shared_ptr<std::atomic<bool>>& cancel) DataBase priv::create_emboss_data_base(const std::string &text,
StyleManager &style_manager,
TextLinesModel &text_lines,
const Selection &selection,
ModelVolumeType type,
std::shared_ptr<std::atomic<bool>> &cancel)
{ {
// create volume_name // create volume_name
std::string volume_name = text; // copy std::string volume_name = text; // copy
@ -3594,6 +3694,8 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st
} else } else
text_lines.reset(); text_lines.reset();
bool is_outside = (type == ModelVolumeType::MODEL_PART);
// Cancel previous Job, when it is in process // Cancel previous Job, when it is in process
// worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs // worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs
// Cancel only EmbossUpdateJob no others // Cancel only EmbossUpdateJob no others
@ -3601,7 +3703,7 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st
cancel->store(true); cancel->store(true);
// create new shared ptr to cancel new job // create new shared ptr to cancel new job
cancel = std::make_shared<std::atomic<bool>>(false); cancel = std::make_shared<std::atomic<bool>>(false);
return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), tc, volume_name, cancel, text_lines.get_lines()}; return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), tc, volume_name, is_outside, cancel, text_lines.get_lines()};
} }
void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor) void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor)
@ -3639,10 +3741,7 @@ void priv::start_create_volume_job(const ModelObject *object,
if (sources.empty()) { if (sources.empty()) {
use_surface = false; use_surface = false;
} else { } else {
bool is_outside = volume_type == ModelVolumeType::MODEL_PART; SurfaceVolumeData sfvd{volume_trmat, std::move(sources)};
// check that there is not unexpected volume type
assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER);
SurfaceVolumeData sfvd{volume_trmat, is_outside, std::move(sources)};
CreateSurfaceVolumeData surface_data{std::move(emboss_data), std::move(sfvd), volume_type, object->id()}; CreateSurfaceVolumeData surface_data{std::move(emboss_data), std::move(sfvd), volume_type, object->id()};
job = std::make_unique<CreateSurfaceVolumeJob>(std::move(surface_data)); job = std::make_unique<CreateSurfaceVolumeJob>(std::move(surface_data));
} }
@ -3658,8 +3757,14 @@ void priv::start_create_volume_job(const ModelObject *object,
queue_job(worker, std::move(job)); queue_job(worker, std::move(job));
} }
bool priv::start_create_volume_on_surface_job( bool priv::start_create_volume_on_surface_job(DataBase &emboss_data,
DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster, GLCanvas3D& canvas) ModelVolumeType volume_type,
const Vec2d &screen_coor,
const GLVolume *gl_volume,
RaycastManager &raycaster,
TextLinesModel &text_lines,
StyleManager &style_manager,
GLCanvas3D &canvas)
{ {
assert(gl_volume != nullptr); assert(gl_volume != nullptr);
if (gl_volume == nullptr) return false; if (gl_volume == nullptr) return false;
@ -3670,12 +3775,14 @@ bool priv::start_create_volume_on_surface_job(
int object_idx = gl_volume->object_idx(); int object_idx = gl_volume->object_idx();
if (object_idx < 0 || static_cast<size_t>(object_idx) >= objects.size()) return false; if (object_idx < 0 || static_cast<size_t>(object_idx) >= objects.size()) return false;
ModelObject *obj = objects[object_idx]; const ModelObject *obj_ptr = objects[object_idx];
size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id; if (obj_ptr == nullptr) return false;
const ModelObject &obj = *obj_ptr;
size_t vol_id = obj.volumes[gl_volume->volume_idx()]->id().id;
auto cond = RaycastManager::AllowVolumes({vol_id}); auto cond = RaycastManager::AllowVolumes({vol_id});
RaycastManager::Meshes meshes = create_meshes(canvas, cond); RaycastManager::Meshes meshes = create_meshes(canvas, cond);
raycaster.actualize(*obj, &cond, &meshes); raycaster.actualize(obj, &cond, &meshes);
const Camera &camera = plater->get_camera(); const Camera &camera = plater->get_camera();
std::optional<RaycastManager::Hit> hit = ray_from_camera(raycaster, screen_coor, camera, &cond); std::optional<RaycastManager::Hit> hit = ray_from_camera(raycaster, screen_coor, camera, &cond);
@ -3691,8 +3798,13 @@ bool priv::start_create_volume_on_surface_job(
const FontProp &font_prop = emboss_data.text_configuration.style.prop; const FontProp &font_prop = emboss_data.text_configuration.style.prop;
apply_transformation(font_prop, surface_trmat); apply_transformation(font_prop, surface_trmat);
Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); Transform3d instance = gl_volume->get_instance_transformation().get_matrix();
Transform3d volume_trmat = instance.inverse() * surface_trmat; Transform3d volume_trmat = instance.inverse() * surface_trmat;
start_create_volume_job(obj, volume_trmat, emboss_data, volume_type);
if (font_prop.per_glyph){
init_new_text_line(text_lines, volume_trmat, obj, style_manager);
emboss_data.text_lines = text_lines.get_lines();
}
start_create_volume_job(obj_ptr, volume_trmat, emboss_data, volume_type);
return true; return true;
} }

View File

@ -318,6 +318,7 @@ private:
// Keep information about curvature of text line around surface // Keep information about curvature of text line around surface
TextLinesModel m_text_lines; TextLinesModel m_text_lines;
void reinit_text_lines(unsigned count_lines=0);
// Rotation gizmo // Rotation gizmo
GLGizmoRotate m_rotate_gizmo; GLGizmoRotate m_rotate_gizmo;

View File

@ -29,6 +29,9 @@ namespace priv{
// create sure that emboss object is bigger than source object [in mm] // create sure that emboss object is bigger than source object [in mm]
constexpr float safe_extension = 1.0f; constexpr float safe_extension = 1.0f;
// Offset of clossed side to model
constexpr float SAFE_SURFACE_OFFSET = 0.015f; // [in mm]
/// <summary> /// <summary>
/// Assert check of inputs data /// Assert check of inputs data
/// </summary> /// </summary>
@ -513,7 +516,6 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
double shape_scale = get_shape_scale(prop, *font.font_file); double shape_scale = get_shape_scale(prop, *font.font_file);
double projec_scale = shape_scale / SHAPE_SCALE; double projec_scale = shape_scale / SHAPE_SCALE;
double depth = prop.emboss / projec_scale; double depth = prop.emboss / projec_scale;
auto projectZ = std::make_unique<ProjectZ>(depth);
auto scale_tr = Eigen::Scaling(projec_scale); auto scale_tr = Eigen::Scaling(projec_scale);
// half of font em size for direction of letter emboss // half of font em size for direction of letter emboss
@ -534,16 +536,20 @@ template<typename Fnc> TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w
continue; continue;
Vec2d to_zero_vec = letter_bb.center().cast<double>() * shape_scale; // [in mm] Vec2d to_zero_vec = letter_bb.center().cast<double>() * shape_scale; // [in mm]
auto to_zero = Eigen::Translation<double, 3>(-to_zero_vec.x(), 0., 0.); float surface_offset = input.is_outside ? -priv::SAFE_SURFACE_OFFSET : (-prop.emboss + priv::SAFE_SURFACE_OFFSET);
if (prop.distance.has_value())
surface_offset += *prop.distance;
Eigen::Translation<double, 3> to_zero(-to_zero_vec.x(), 0., static_cast<double>(surface_offset));
const double &angle = angles[i]; const double &angle = angles[i];
auto rotate = Eigen::AngleAxisd(angle + M_PI_2, Vec3d::UnitY()); Eigen::AngleAxisd rotate(angle + M_PI_2, Vec3d::UnitY());
const PolygonPoint &sample = samples[i]; const PolygonPoint &sample = samples[i];
Vec2d offset_vec = unscale(sample.point); // [in mm] Vec2d offset_vec = unscale(sample.point); // [in mm]
auto offset_tr = Eigen::Translation<double, 3>(offset_vec.x(), 0., -offset_vec.y()); Eigen::Translation<double, 3> offset_tr(offset_vec.x(), 0., -offset_vec.y());
Transform3d tr = offset_tr * rotate * to_zero * scale_tr; Transform3d tr = offset_tr * rotate * to_zero * scale_tr;
const ExPolygons &letter_shape = shapes[s_i_offset + i]; const ExPolygons &letter_shape = shapes[s_i_offset + i];
assert(get_extents(letter_shape) == letter_bb); assert(get_extents(letter_shape) == letter_bb);
auto projectZ = std::make_unique<ProjectZ>(depth); auto projectZ = std::make_unique<ProjectZ>(depth);
@ -883,11 +889,9 @@ OrthoProject priv::create_projection_for_cut(
OrthoProject3d priv::create_emboss_projection( OrthoProject3d priv::create_emboss_projection(
bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut) bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut)
{ {
// Offset of clossed side to model
const float surface_offset = 0.015f; // [in mm]
float float
front_move = (is_outside) ? emboss : surface_offset, front_move = (is_outside) ? emboss : SAFE_SURFACE_OFFSET,
back_move = -((is_outside) ? surface_offset : emboss); back_move = -((is_outside) ? SAFE_SURFACE_OFFSET : emboss);
its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move))); its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move)));
Vec3d from_front_to_back(0., 0., back_move - front_move); Vec3d from_front_to_back(0., 0., back_move - front_move);
return OrthoProject3d(from_front_to_back); return OrthoProject3d(from_front_to_back);
@ -895,7 +899,7 @@ OrthoProject3d priv::create_emboss_projection(
namespace { namespace {
indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d& tr,const SurfaceVolumeData::ModelSources &sources, bool is_outside, DataBase& input, std::function<bool()> was_canceled) { indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d& tr,const SurfaceVolumeData::ModelSources &sources, DataBase& input, std::function<bool()> was_canceled) {
assert(!sources.empty()); assert(!sources.empty());
BoundingBox bb = get_extents(shapes); BoundingBox bb = get_extents(shapes);
const FontFile &ff = *input.font_file.font_file; const FontFile &ff = *input.font_file.font_file;
@ -985,7 +989,7 @@ indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transfor
if (was_canceled()) return {}; if (was_canceled()) return {};
// !! Projection needs to transform cut // !! Projection needs to transform cut
OrthoProject3d projection = priv::create_emboss_projection(is_outside, fp.emboss, emboss_tr, cut); OrthoProject3d projection = priv::create_emboss_projection(input.is_outside, fp.emboss, emboss_tr, cut);
return cut2model(cut, projection); return cut2model(cut, projection);
} }
@ -1041,7 +1045,7 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
Transform3d modify = offset_tr * rotate; Transform3d modify = offset_tr * rotate;
Transform3d tr = input2.text_tr * modify; Transform3d tr = input2.text_tr * modify;
indexed_triangle_set glyph_its = cut_surface_to_its(glyph_shape, tr, input2.sources, input2.is_outside, input1, was_canceled); indexed_triangle_set glyph_its = cut_surface_to_its(glyph_shape, tr, input2.sources, input1, was_canceled);
// move letter in volume on the right position // move letter in volume on the right position
its_transform(glyph_its, modify); its_transform(glyph_its, modify);
@ -1074,7 +1078,7 @@ TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2
if (shapes.empty()) if (shapes.empty())
throw JobException(_u8L("Font doesn't have any shape for given text.").c_str()); throw JobException(_u8L("Font doesn't have any shape for given text.").c_str());
indexed_triangle_set its = cut_surface_to_its(shapes, input2.text_tr, input2.sources, input2.is_outside, input1, was_canceled); indexed_triangle_set its = cut_surface_to_its(shapes, input2.text_tr, input2.sources, input1, was_canceled);
if (was_canceled()) return {}; if (was_canceled()) return {};
if (its.empty()) if (its.empty())
throw JobException(_u8L("There is no valid surface for text projection.").c_str()); throw JobException(_u8L("There is no valid surface for text projection.").c_str());

View File

@ -29,6 +29,11 @@ struct DataBase
// new volume name created from text // new volume name created from text
std::string volume_name; std::string volume_name;
// Define projection move
// True (raised) .. move outside from surface
// False (engraved).. move into object
bool is_outside;
// flag that job is canceled // flag that job is canceled
// for time after process. // for time after process.
std::shared_ptr<std::atomic<bool>> cancel; std::shared_ptr<std::atomic<bool>> cancel;
@ -158,11 +163,6 @@ struct SurfaceVolumeData
// Transformation of text volume inside of object // Transformation of text volume inside of object
Transform3d text_tr; Transform3d text_tr;
// Define projection move
// True (raised) .. move outside from surface
// False (engraved).. move into object
bool is_outside;
struct ModelSource struct ModelSource
{ {
// source volumes // source volumes

View File

@ -251,62 +251,24 @@ GLModel::Geometry create_geometry(const TextLines &lines)
} }
} // namespace } // namespace
void TextLinesModel::init(const Selection &selection, double line_height, unsigned count_lines) void TextLinesModel::init(const Transform3d &text_tr, const ModelVolumePtrs &volumes_to_slice, FontProp::Align align, double line_height, unsigned count_lines)
{ {
m_model.reset(); m_model.reset();
m_lines.clear(); m_lines.clear();
const GLVolume *gl_volume_ptr = selection.get_first_volume();
if (gl_volume_ptr == nullptr)
return;
const GLVolume &gl_volume = *gl_volume_ptr;
const ModelObjectPtrs &objects = selection.get_model()->objects;
const ModelObject *mo_ptr = get_model_object(gl_volume, objects);
if (mo_ptr == nullptr)
return;
const ModelObject &mo = *mo_ptr;
const ModelVolume *mv_ptr = get_model_volume(gl_volume, objects);
if (mv_ptr == nullptr)
return;
const ModelVolume &mv = *mv_ptr;
if (mv.is_the_only_one_part())
return;
// calculate count lines when not set
if (count_lines == 0) {
const std::optional<TextConfiguration> tc_opt = mv.text_configuration;
if (!tc_opt.has_value())
return;
count_lines = Emboss::get_count_lines(tc_opt->text);
if (count_lines == 0)
return;
}
double first_line_center = offset + (count_lines / 2) * line_height - ((count_lines % 2 == 0) ? line_height / 2. : 0.); double first_line_center = offset + (count_lines / 2) * line_height - ((count_lines % 2 == 0) ? line_height / 2. : 0.);
std::vector<float> line_centers(count_lines); std::vector<float> line_centers(count_lines);
for (size_t i = 0; i < count_lines; ++i) for (size_t i = 0; i < count_lines; ++i)
line_centers[i] = static_cast<float>(first_line_center - i * line_height); line_centers[i] = static_cast<float>(first_line_center - i * line_height);
const Transform3d &mv_trafo = gl_volume.get_volume_transformation().get_matrix();
// contour transformation // contour transformation
Transform3d c_trafo = mv_trafo * get_rotation(); Transform3d c_trafo = text_tr * get_rotation();
Transform3d c_trafo_inv = c_trafo.inverse(); Transform3d c_trafo_inv = c_trafo.inverse();
std::vector<Polygons> line_contours(count_lines); std::vector<Polygons> line_contours(count_lines);
for (const ModelVolume *volume : mo.volumes) { for (const ModelVolume *volume : volumes_to_slice) {
// only part could be surface for volumes
if (!volume->is_model_part())
continue;
// is selected volume
if (mv.id() == volume->id())
continue;
MeshSlicingParams slicing_params; MeshSlicingParams slicing_params;
slicing_params.trafo = c_trafo_inv * volume->get_matrix(); slicing_params.trafo = c_trafo_inv * volume->get_matrix();
for (size_t i = 0; i < count_lines; ++i) { for (size_t i = 0; i < count_lines; ++i) {
const Polygons polys = Slic3r::slice_mesh(volume->mesh().its, line_centers[i], slicing_params); const Polygons polys = Slic3r::slice_mesh(volume->mesh().its, line_centers[i], slicing_params);
if (polys.empty()) if (polys.empty())
@ -328,6 +290,7 @@ void TextLinesModel::init(const Selection &selection, double line_height, unsign
return; return;
m_model.init_from(std::move(geometry)); m_model.init_from(std::move(geometry));
/*/ /*/
// slower solution
ColorRGBA color(.7f, .7f, .7f, .7f); // Transparent Gray ColorRGBA color(.7f, .7f, .7f, .7f); // Transparent Gray
m_model.set_color(color); m_model.set_color(color);
m_model.init_from(create_its(m_lines)); m_model.init_from(create_its(m_lines));

View File

@ -7,27 +7,35 @@
#include <libslic3r/Emboss.hpp> #include <libslic3r/Emboss.hpp>
#include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/GLModel.hpp"
namespace Slic3r {
class ModelVolume;
typedef std::vector<ModelVolume *> ModelVolumePtrs;
}
namespace Slic3r::GUI { namespace Slic3r::GUI {
class Selection;
class TextLinesModel class TextLinesModel
{ {
public: public:
// line offset in y direction (up/down) // line offset in y direction (up/down)
float offset = 0; float offset = 0;
/// <summary> /// <summary>
/// Initialize model and lines /// Initialize model and lines
/// </summary> /// </summary>
/// <param name="selection">Must be selected text volume</param> /// <param name="text_tr">Transformation of text volume inside object (aka inside of instance)</param>
/// <param name="line_height">Height of text line with spacing [in mm]</param> /// <param name="volumes_to_slice">Vector of volumes to be sliced</param>
/// <param name="line_offset">Offset of base line from center [in mm]</param> /// <param name="align">Vertical (Y) align of the text</param>
/// <param name="count_lines">[Optional] Count lines when not set it is calculated from vodel volume text</param> /// <param name="line_height">Distance between lines [in mm]</param>
void init(const Selection &selection, double line_height, unsigned count_lines = 0); /// <param name="count_lines">Count lines(slices over volumes)</param>
void init(const Transform3d &text_tr, const ModelVolumePtrs& volumes_to_slice, FontProp::Align align, double line_height, unsigned count_lines);
void render(const Transform3d &text_world); void render(const Transform3d &text_world);
bool is_init() const { return m_model.is_initialized(); } bool is_init() const { return m_model.is_initialized(); }
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(const Slic3r::Emboss::FontFile& ff, const FontProp& fp);
static double calc_line_height(const Slic3r::Emboss::FontFile& ff, const FontProp& fp); // return lineheight in mm
private: private:
Slic3r::Emboss::TextLines m_lines; Slic3r::Emboss::TextLines m_lines;

View File

@ -282,6 +282,25 @@ void StyleManager::clear_glyphs_cache()
void StyleManager::clear_imgui_font() { m_style_cache.atlas.Clear(); } void StyleManager::clear_imgui_font() { m_style_cache.atlas.Clear(); }
#include "slic3r/GUI/TextLines.hpp"
double StyleManager::get_line_height()
{
assert(is_active_font());
if (!is_active_font())
return -1;
const auto &ffc = get_font_file_with_cache();
assert(ffc.has_value());
if (!ffc.has_value())
return -1;
const auto &ff_ptr = ffc.font_file;
assert(ff_ptr != nullptr);
if (ff_ptr == nullptr)
return -1;
const FontProp &fp = get_font_prop();
const FontFile &ff = *ff_ptr;
return TextLinesModel::calc_line_height(ff, fp);
}
ImFont *StyleManager::get_imgui_font() ImFont *StyleManager::get_imgui_font()
{ {
if (!is_active_font()) return nullptr; if (!is_active_font()) return nullptr;

View File

@ -106,6 +106,10 @@ public:
// remove cached imgui font for actual selected font // remove cached imgui font for actual selected font
void clear_imgui_font(); void clear_imgui_font();
// calculate line height
// not const because access to font file which could be created.
double get_line_height(); /* const */
// getters for private data // getters for private data
const EmbossStyle *get_stored_style() const; const EmbossStyle *get_stored_style() const;