mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 01:29:00 +08:00
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:
parent
496cc54bd7
commit
d2a605ee2e
@ -148,12 +148,14 @@ namespace priv {
|
||||
/// <param name="style_manager">Keep actual selected style</param>
|
||||
/// <param name="text_lines">Needed when 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>
|
||||
/// <returns>Base data for emboss text</returns>
|
||||
static DataBase 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);
|
||||
|
||||
/// <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="gl_volume">Volume to find surface for create</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>
|
||||
/// <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,
|
||||
@ -183,6 +187,8 @@ static bool start_create_volume_on_surface_job(DataBase &emboss_data,
|
||||
const Vec2d &screen_coor,
|
||||
const GLVolume *gl_volume,
|
||||
RaycastManager &raycaster,
|
||||
TextLinesModel &text_lines,
|
||||
/*const */ StyleManager &style_manager,
|
||||
GLCanvas3D &canvas);
|
||||
|
||||
/// <summary>
|
||||
@ -249,7 +255,29 @@ static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas, bool keep
|
||||
} // namespace priv
|
||||
|
||||
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);
|
||||
// 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)
|
||||
@ -258,11 +286,11 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
|
||||
return;
|
||||
|
||||
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;
|
||||
if (gl_volume != nullptr && !is_simple_mode) {
|
||||
// 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.
|
||||
// So add part near by in simmilar manner as right panel do
|
||||
create_volume(volume_type);
|
||||
@ -285,7 +313,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
|
||||
|
||||
Size s = m_parent.get_canvas_size();
|
||||
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;
|
||||
bool is_simple_mode = wxGetApp().get_mode() == comSimple;
|
||||
// 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);
|
||||
if (vol == nullptr) {
|
||||
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
|
||||
// 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
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1062,26 +1094,65 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
|
||||
namespace {
|
||||
void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines)
|
||||
{
|
||||
assert(style_manager.is_active_font());
|
||||
if (!style_manager.is_active_font())
|
||||
double line_height = style_manager.get_line_height();
|
||||
if (line_height < 0)
|
||||
return;
|
||||
const auto &ffc = style_manager.get_font_file_with_cache();
|
||||
assert(ffc.has_value());
|
||||
if (!ffc.has_value())
|
||||
|
||||
const GLVolume *gl_volume_ptr = selection.get_first_volume();
|
||||
if (gl_volume_ptr == nullptr)
|
||||
return;
|
||||
const auto &ff_ptr = ffc.font_file;
|
||||
assert(ff_ptr != nullptr);
|
||||
if (ff_ptr == nullptr)
|
||||
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;
|
||||
|
||||
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 FontFile &ff = *ff_ptr;
|
||||
|
||||
double line_height = TextLinesModel::calc_line_height(ff, fp);
|
||||
text_lines.init(selection, line_height, count_lines);
|
||||
text_lines.init(mv_trafo, volumes, fp.align, 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()
|
||||
{
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
@ -1221,7 +1292,7 @@ void GLGizmoEmboss::set_volume_by_selection()
|
||||
m_volume_id = volume->id();
|
||||
|
||||
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
|
||||
assert(m_style_manager.is_active_font());
|
||||
@ -1304,7 +1375,7 @@ bool GLGizmoEmboss::process()
|
||||
// exist loaded font file?
|
||||
if (!m_style_manager.is_active_font()) return false;
|
||||
|
||||
DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_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()};
|
||||
std::unique_ptr<Job> job = nullptr;
|
||||
|
||||
@ -1338,7 +1409,7 @@ bool GLGizmoEmboss::process()
|
||||
// check that there is not unexpected volume type
|
||||
assert(is_outside || m_volume->is_negative_volume() ||
|
||||
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));
|
||||
} else {
|
||||
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);
|
||||
if (count_lines != m_text_lines.get_lines().size())
|
||||
// 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();
|
||||
range_text = create_range_text_prep();
|
||||
@ -1982,6 +2053,8 @@ void GLGizmoEmboss::draw_font_list_line()
|
||||
|
||||
if (exist_change) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -2206,7 +2279,8 @@ void GLGizmoEmboss::draw_model_type()
|
||||
m_volume->set_type(*new_type);
|
||||
|
||||
// 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
|
||||
bool is_volume_move_inside = (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))
|
||||
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
|
||||
// store font size into path serialization
|
||||
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;
|
||||
if (rev_checkbox(tr.per_glyph, per_glyph, def_per_glyph,
|
||||
_u8L("Revert Transformation per glyph."))) {
|
||||
if (per_glyph && !m_text_lines.is_init())
|
||||
reinit_text_lines();
|
||||
process();
|
||||
} else if (ImGui::IsItemHovered()) {
|
||||
if (per_glyph) {
|
||||
@ -3054,7 +3133,7 @@ void GLGizmoEmboss::draw_advanced()
|
||||
} else {
|
||||
ImGui::SetTooltip("%s", _u8L("Set position and orientation per Glyph.").c_str());
|
||||
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())
|
||||
m_text_lines.reset();
|
||||
@ -3064,10 +3143,10 @@ void GLGizmoEmboss::draw_advanced()
|
||||
ImGui::SameLine();
|
||||
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")) {
|
||||
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();
|
||||
} 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
|
||||
|
||||
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)){
|
||||
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
|
||||
process();
|
||||
}
|
||||
@ -3132,6 +3213,8 @@ void GLGizmoEmboss::draw_advanced()
|
||||
m_volume->text_configuration->style.prop.line_gap != font_prop.line_gap) {
|
||||
// line gap is planed to be stored inside of imgui font atlas
|
||||
m_style_manager.clear_imgui_font();
|
||||
if (font_prop.per_glyph)
|
||||
reinit_text_lines(m_text_lines.get_lines().size());
|
||||
exist_change = true;
|
||||
}
|
||||
}
|
||||
@ -3198,9 +3281,13 @@ void GLGizmoEmboss::draw_advanced()
|
||||
}
|
||||
|
||||
if (is_moved){
|
||||
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));
|
||||
if (font_prop.per_glyph){
|
||||
process();
|
||||
} 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();
|
||||
|
||||
@ -3234,8 +3321,11 @@ void GLGizmoEmboss::draw_advanced()
|
||||
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);
|
||||
|
||||
if (font_prop.per_glyph)
|
||||
reinit_text_lines(m_text_lines.get_lines().size());
|
||||
|
||||
// recalculate for surface cut
|
||||
if (font_prop.use_surface)
|
||||
if (font_prop.use_surface || font_prop.per_glyph)
|
||||
process();
|
||||
}
|
||||
|
||||
@ -3283,14 +3373,19 @@ void GLGizmoEmboss::draw_advanced()
|
||||
|
||||
if (exist_change) {
|
||||
m_style_manager.clear_glyphs_cache();
|
||||
if (m_style_manager.get_font_prop().per_glyph)
|
||||
reinit_text_lines();
|
||||
else
|
||||
m_text_lines.reset();
|
||||
process();
|
||||
}
|
||||
|
||||
if (ImGui::Button(_u8L("Set text to face camera").c_str())) {
|
||||
assert(get_selected_volume(m_parent.get_selection()) == m_volume);
|
||||
const Camera &cam = wxGetApp().plater()->get_camera();
|
||||
bool use_surface = m_style_manager.get_font_prop().use_surface;
|
||||
if (priv::apply_camera_dir(cam, m_parent, m_keep_up) && use_surface)
|
||||
const FontProp &prop = m_style_manager.get_font_prop();
|
||||
if (priv::apply_camera_dir(cam, m_parent, m_keep_up) &&
|
||||
prop.use_surface || prop.per_glyph)
|
||||
process();
|
||||
} else if (ImGui::IsItemHovered()) {
|
||||
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
|
||||
///////////////
|
||||
|
||||
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
|
||||
std::string volume_name = text; // copy
|
||||
@ -3594,6 +3694,8 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st
|
||||
} else
|
||||
text_lines.reset();
|
||||
|
||||
bool is_outside = (type == ModelVolumeType::MODEL_PART);
|
||||
|
||||
// Cancel previous Job, when it is in process
|
||||
// worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs
|
||||
// Cancel only EmbossUpdateJob no others
|
||||
@ -3601,7 +3703,7 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st
|
||||
cancel->store(true);
|
||||
// create new shared ptr to cancel new job
|
||||
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)
|
||||
@ -3639,10 +3741,7 @@ void priv::start_create_volume_job(const ModelObject *object,
|
||||
if (sources.empty()) {
|
||||
use_surface = false;
|
||||
} else {
|
||||
bool is_outside = volume_type == ModelVolumeType::MODEL_PART;
|
||||
// check that there is not unexpected volume type
|
||||
assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER);
|
||||
SurfaceVolumeData sfvd{volume_trmat, is_outside, std::move(sources)};
|
||||
SurfaceVolumeData sfvd{volume_trmat, std::move(sources)};
|
||||
CreateSurfaceVolumeData surface_data{std::move(emboss_data), std::move(sfvd), volume_type, object->id()};
|
||||
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));
|
||||
}
|
||||
|
||||
bool priv::start_create_volume_on_surface_job(
|
||||
DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster, GLCanvas3D& canvas)
|
||||
bool priv::start_create_volume_on_surface_job(DataBase &emboss_data,
|
||||
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);
|
||||
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();
|
||||
if (object_idx < 0 || static_cast<size_t>(object_idx) >= objects.size()) return false;
|
||||
ModelObject *obj = objects[object_idx];
|
||||
size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id;
|
||||
const ModelObject *obj_ptr = objects[object_idx];
|
||||
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});
|
||||
|
||||
RaycastManager::Meshes meshes = create_meshes(canvas, cond);
|
||||
raycaster.actualize(*obj, &cond, &meshes);
|
||||
raycaster.actualize(obj, &cond, &meshes);
|
||||
|
||||
const Camera &camera = plater->get_camera();
|
||||
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;
|
||||
apply_transformation(font_prop, surface_trmat);
|
||||
Transform3d instance = gl_volume->get_instance_transformation().get_matrix();
|
||||
Transform3d volume_trmat = instance.inverse() * surface_trmat;
|
||||
start_create_volume_job(obj, volume_trmat, emboss_data, volume_type);
|
||||
Transform3d volume_trmat = instance.inverse() * surface_trmat;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -318,6 +318,7 @@ private:
|
||||
|
||||
// Keep information about curvature of text line around surface
|
||||
TextLinesModel m_text_lines;
|
||||
void reinit_text_lines(unsigned count_lines=0);
|
||||
|
||||
// Rotation gizmo
|
||||
GLGizmoRotate m_rotate_gizmo;
|
||||
|
@ -29,6 +29,9 @@ namespace priv{
|
||||
// create sure that emboss object is bigger than source object [in mm]
|
||||
constexpr float safe_extension = 1.0f;
|
||||
|
||||
// Offset of clossed side to model
|
||||
constexpr float SAFE_SURFACE_OFFSET = 0.015f; // [in mm]
|
||||
|
||||
/// <summary>
|
||||
/// Assert check of inputs data
|
||||
/// </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 projec_scale = shape_scale / SHAPE_SCALE;
|
||||
double depth = prop.emboss / projec_scale;
|
||||
auto projectZ = std::make_unique<ProjectZ>(depth);
|
||||
auto scale_tr = Eigen::Scaling(projec_scale);
|
||||
|
||||
// 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;
|
||||
|
||||
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];
|
||||
auto rotate = Eigen::AngleAxisd(angle + M_PI_2, Vec3d::UnitY());
|
||||
Eigen::AngleAxisd rotate(angle + M_PI_2, Vec3d::UnitY());
|
||||
|
||||
const PolygonPoint &sample = samples[i];
|
||||
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;
|
||||
|
||||
const ExPolygons &letter_shape = shapes[s_i_offset + i];
|
||||
assert(get_extents(letter_shape) == letter_bb);
|
||||
auto projectZ = std::make_unique<ProjectZ>(depth);
|
||||
@ -883,11 +889,9 @@ OrthoProject priv::create_projection_for_cut(
|
||||
OrthoProject3d priv::create_emboss_projection(
|
||||
bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut)
|
||||
{
|
||||
// Offset of clossed side to model
|
||||
const float surface_offset = 0.015f; // [in mm]
|
||||
float
|
||||
front_move = (is_outside) ? emboss : surface_offset,
|
||||
back_move = -((is_outside) ? surface_offset : emboss);
|
||||
front_move = (is_outside) ? emboss : SAFE_SURFACE_OFFSET,
|
||||
back_move = -((is_outside) ? SAFE_SURFACE_OFFSET : emboss);
|
||||
its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move)));
|
||||
Vec3d from_front_to_back(0., 0., back_move - front_move);
|
||||
return OrthoProject3d(from_front_to_back);
|
||||
@ -895,7 +899,7 @@ OrthoProject3d priv::create_emboss_projection(
|
||||
|
||||
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());
|
||||
BoundingBox bb = get_extents(shapes);
|
||||
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 {};
|
||||
|
||||
// !! 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);
|
||||
}
|
||||
|
||||
@ -1041,7 +1045,7 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in
|
||||
|
||||
Transform3d modify = offset_tr * rotate;
|
||||
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
|
||||
its_transform(glyph_its, modify);
|
||||
|
||||
@ -1074,7 +1078,7 @@ TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2
|
||||
if (shapes.empty())
|
||||
throw JobException(_u8L("Font doesn't have any shape for given text.").c_str());
|
||||
|
||||
indexed_triangle_set its = cut_surface_to_its(shapes, input2.text_tr, input2.sources, 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 (its.empty())
|
||||
throw JobException(_u8L("There is no valid surface for text projection.").c_str());
|
||||
|
@ -29,6 +29,11 @@ struct DataBase
|
||||
// new volume name created from text
|
||||
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
|
||||
// for time after process.
|
||||
std::shared_ptr<std::atomic<bool>> cancel;
|
||||
@ -158,11 +163,6 @@ struct SurfaceVolumeData
|
||||
// Transformation of text volume inside of object
|
||||
Transform3d text_tr;
|
||||
|
||||
// Define projection move
|
||||
// True (raised) .. move outside from surface
|
||||
// False (engraved).. move into object
|
||||
bool is_outside;
|
||||
|
||||
struct ModelSource
|
||||
{
|
||||
// source volumes
|
||||
|
@ -251,62 +251,24 @@ GLModel::Geometry create_geometry(const TextLines &lines)
|
||||
}
|
||||
} // 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_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.);
|
||||
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);
|
||||
|
||||
const Transform3d &mv_trafo = gl_volume.get_volume_transformation().get_matrix();
|
||||
|
||||
// contour transformation
|
||||
Transform3d c_trafo = mv_trafo * get_rotation();
|
||||
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 : mo.volumes) {
|
||||
// only part could be surface for volumes
|
||||
if (!volume->is_model_part())
|
||||
continue;
|
||||
|
||||
// is selected volume
|
||||
if (mv.id() == volume->id())
|
||||
continue;
|
||||
|
||||
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())
|
||||
@ -328,6 +290,7 @@ void TextLinesModel::init(const Selection &selection, double line_height, unsign
|
||||
return;
|
||||
m_model.init_from(std::move(geometry));
|
||||
/*/
|
||||
// slower solution
|
||||
ColorRGBA color(.7f, .7f, .7f, .7f); // Transparent Gray
|
||||
m_model.set_color(color);
|
||||
m_model.init_from(create_its(m_lines));
|
||||
|
@ -7,27 +7,35 @@
|
||||
#include <libslic3r/Emboss.hpp>
|
||||
#include "slic3r/GUI/GLModel.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
class ModelVolume;
|
||||
typedef std::vector<ModelVolume *> ModelVolumePtrs;
|
||||
}
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
class Selection;
|
||||
class TextLinesModel
|
||||
{
|
||||
public:
|
||||
// line offset in y direction (up/down)
|
||||
float offset = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize model and lines
|
||||
/// </summary>
|
||||
/// <param name="selection">Must be selected text volume</param>
|
||||
/// <param name="line_height">Height of text line with spacing [in mm]</param>
|
||||
/// <param name="line_offset">Offset of base line from center [in mm]</param>
|
||||
/// <param name="count_lines">[Optional] Count lines when not set it is calculated from vodel volume text</param>
|
||||
void init(const Selection &selection, double line_height, unsigned count_lines = 0);
|
||||
/// <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="align">Vertical (Y) align of the text</param>
|
||||
/// <param name="line_height">Distance between lines [in mm]</param>
|
||||
/// <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);
|
||||
|
||||
bool is_init() const { return m_model.is_initialized(); }
|
||||
void reset() { m_model.reset(); m_lines.clear(); }
|
||||
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:
|
||||
Slic3r::Emboss::TextLines m_lines;
|
||||
|
||||
|
@ -282,6 +282,25 @@ void StyleManager::clear_glyphs_cache()
|
||||
|
||||
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()
|
||||
{
|
||||
if (!is_active_font()) return nullptr;
|
||||
|
@ -106,6 +106,10 @@ public:
|
||||
// remove cached imgui font for actual selected 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
|
||||
const EmbossStyle *get_stored_style() const;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user