Create text on second Part(volume) of object

This commit is contained in:
Filip Sykala 2022-02-01 18:31:27 +01:00
parent 936ba6c28c
commit 1078fe55ec
7 changed files with 151 additions and 52 deletions

View File

@ -103,6 +103,15 @@ struct FontItem
wx_mac_font_descr // path is font descriptor generated by wxWidgets on windows wx_mac_font_descr // path is font descriptor generated by wxWidgets on windows
}; };
bool operator==(const FontItem &other) const {
return
type == other.type &&
prop == other.prop &&
name == other.name &&
path == other.path
;
}
// undo / redo stack recovery // undo / redo stack recovery
template<class Archive> void serialize(Archive &ar) template<class Archive> void serialize(Archive &ar)
{ {

View File

@ -24,6 +24,7 @@
#include "libslic3r/AppConfig.hpp" // store/load font list #include "libslic3r/AppConfig.hpp" // store/load font list
#include "libslic3r/MapUtils.hpp" #include "libslic3r/MapUtils.hpp"
#include "libslic3r/Format/OBJ.hpp" // load obj file for default object #include "libslic3r/Format/OBJ.hpp" // load obj file for default object
#include "libslic3r/BuildVolume.hpp"
#include "imgui/imgui_stdlib.h" // using std::string for inputs #include "imgui/imgui_stdlib.h" // using std::string for inputs
#include "nanosvg/nanosvg.h" // load SVG file #include "nanosvg/nanosvg.h" // load SVG file
@ -133,15 +134,28 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
} }
std::optional<int> object_idx; std::optional<int> object_idx;
std::optional<Transform3d> hit_vol_tr;
const Selection &selection = m_parent.get_selection(); const Selection &selection = m_parent.get_selection();
if (!selection.is_empty()) object_idx = selection.get_object_idx(); if (!selection.is_empty()) {
auto data = object_idx = selection.get_object_idx();
std::make_unique<EmbossDataCreate>(m_font_manager.get_font_file(), int hovered_id = m_parent.get_first_hover_volume_idx();
create_configuration(), if (hovered_id >= 0) {
create_volume_name(), volume_type, GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id];
screen_coor, object_idx, hit_vol_tr = gl_volume->get_instance_transformation().get_matrix();
&m_raycast_manager); }
auto &worker = wxGetApp().plater()->get_ui_job_worker(); }
Plater* plater = wxGetApp().plater();
const Camera &camera = plater->get_camera();
auto data = std::make_unique<EmbossDataCreate>(
m_font_manager.get_font_file(),
create_configuration(),
create_volume_name(), volume_type, screen_coor, object_idx,
hit_vol_tr, camera,
plater->build_volume().bed_shape(),
&m_raycast_manager);
auto &worker = plater->get_ui_job_worker();
queue_job(worker, std::make_unique<EmbossCreateJob>(std::move(data))); queue_job(worker, std::make_unique<EmbossCreateJob>(std::move(data)));
} }
@ -206,14 +220,15 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
// initialize raycasters // initialize raycasters
// IMPROVE: move to job, for big scene it slows down // IMPROVE: move to job, for big scene it slows down
ModelObject *act_model_object = act_model_volume->get_object(); ModelObject *act_model_object = act_model_volume->get_object();
m_raycast_manager.actualize({act_model_object}, &skip); m_raycast_manager.actualize(act_model_object, &skip);
return false; return false;
} }
// wxCoord == int --> wx/types.h // wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>(); Vec2d mouse_pos = mouse_coord.cast<double>();
auto hit = m_raycast_manager.unproject(mouse_pos, &skip); const Camera &camera = wxGetApp().plater()->get_camera();
auto hit = m_raycast_manager.unproject(mouse_pos, camera, &skip);
if (!hit.has_value()) { if (!hit.has_value()) {
// there is no hit // there is no hit
// show common translation of object // show common translation of object
@ -325,7 +340,7 @@ void GLGizmoEmboss::on_render_for_picking() {
void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
{ {
initialize(); initialize();
check_selection(); check_selection();
// TODO: fix width - showing scroll in first draw of advanced. // TODO: fix width - showing scroll in first draw of advanced.
const ImVec2 &min_window_size = get_minimal_window_size(); const ImVec2 &min_window_size = get_minimal_window_size();
@ -480,6 +495,12 @@ void GLGizmoEmboss::initialize()
cfg.max_style_image_width = cfg.max_font_name_width - cfg.max_style_image_width = cfg.max_font_name_width -
2 * style.FramePadding.x; 2 * style.FramePadding.x;
// initialize default font
FontList default_font_list = create_default_font_list();
for (const FontItem &fi : default_font_list) {
assert(cfg.default_styles.find(fi.name) == cfg.default_styles.end());
cfg.default_styles[fi.name] = fi; // copy
}
m_gui_cfg.emplace(cfg); m_gui_cfg.emplace(cfg);
// TODO: What to do when icon was NOT loaded? Generate them? // TODO: What to do when icon was NOT loaded? Generate them?
@ -489,39 +510,28 @@ void GLGizmoEmboss::initialize()
const AppConfig *app_cfg = wxGetApp().app_config; const AppConfig *app_cfg = wxGetApp().app_config;
FontList font_list = load_font_list_from_app_config(app_cfg); FontList font_list = load_font_list_from_app_config(app_cfg);
m_font_manager.add_fonts(font_list); m_font_manager.add_fonts(font_list);
// TODO: select last session sellected font index
if (!m_font_manager.load_first_valid_font()) { if (!m_font_manager.load_first_valid_font()) {
FontList font_list = create_default_font_list(); m_font_manager.add_fonts(default_font_list);
m_font_manager.add_fonts(font_list);
// TODO: What to do when default fonts are not loadable? // TODO: What to do when default fonts are not loadable?
bool success = m_font_manager.load_first_valid_font(); bool success = m_font_manager.load_first_valid_font();
assert(success); assert(success);
} }
set_default_text(); set_default_text();
select_stored_font_item();
} }
FontList GLGizmoEmboss::create_default_font_list() FontList GLGizmoEmboss::create_default_font_list()
{ {
// https://docs.wxwidgets.org/3.0/classwx_font.html // https://docs.wxwidgets.org/3.0/classwx_font.html
// Predefined objects/pointers: wxNullFont, wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT, wxSWISS_FONT // Predefined objects/pointers: wxNullFont, wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT, wxSWISS_FONT
FontItem par_fi = WxFontUtils::get_font_item(*wxNORMAL_FONT, _u8L("Parallel to bed"));
FontItem perp_fi = WxFontUtils::get_font_item(*wxNORMAL_FONT, _u8L("Perpendicular to bed"));
FontItem fix_fi = WxFontUtils::get_font_item(*wxNORMAL_FONT, _u8L("Fixed size"));
FontItem negative_fi = WxFontUtils::get_font_item(*wxNORMAL_FONT, _u8L("Negative"));
return { return {
par_fi, WxFontUtils::get_font_item(*wxNORMAL_FONT, _u8L("NORMAL")), // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)
perp_fi, WxFontUtils::get_font_item(*wxSMALL_FONT, _u8L("SMALL")), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT.
fix_fi, WxFontUtils::get_font_item(*wxITALIC_FONT, _u8L("ITALIC")), // A font using the wxFONTFAMILY_ROMAN family and wxFONTSTYLE_ITALIC style and of the same size of wxNORMAL_FONT.
negative_fi, WxFontUtils::get_font_item(*wxSWISS_FONT, _u8L("SWISS")), // A font identic to wxNORMAL_FONT except for the family used which is wxFONTFAMILY_SWISS.
WxFontUtils::get_font_item(*wxNORMAL_FONT), // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) WxFontUtils::get_font_item(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), _u8L("MODERN"))
WxFontUtils::get_font_item(*wxSMALL_FONT), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT.
WxFontUtils::get_font_item(*wxITALIC_FONT), // A font using the wxFONTFAMILY_ROMAN family and wxFONTSTYLE_ITALIC style and of the same size of wxNORMAL_FONT.
WxFontUtils::get_font_item(*wxSWISS_FONT), // A font identic to wxNORMAL_FONT except for the family used which is wxFONTFAMILY_SWISS.
WxFontUtils::get_font_item(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD))
//, WxFontUtils::get_os_font() == wxNORMAL_FONT //, WxFontUtils::get_os_font() == wxNORMAL_FONT
}; };
} }
@ -617,6 +627,18 @@ void GLGizmoEmboss::close()
m_parent.get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss); m_parent.get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss);
} }
void GLGizmoEmboss::select_stored_font_item()
{
const std::string &name = m_font_manager.get_font_item().name;
const auto &styles = m_gui_cfg->default_styles;
const auto &it = styles.find(name);
if (it == styles.end()) {
m_stored_font_item.reset();
return;
}
m_stored_font_item = it->second;
}
void GLGizmoEmboss::draw_window() void GLGizmoEmboss::draw_window()
{ {
#ifdef ALLOW_DEBUG_MODE #ifdef ALLOW_DEBUG_MODE
@ -899,6 +921,7 @@ void GLGizmoEmboss::draw_rename_style(bool start_rename)
m_font_manager.get_truncated_name() = ""; m_font_manager.get_truncated_name() = "";
m_font_manager.free_style_images(); m_font_manager.free_style_images();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
select_stored_font_item();
} }
ImGui::EndPopup(); ImGui::EndPopup();
} }
@ -936,7 +959,10 @@ void GLGizmoEmboss::draw_style_list() {
const FontManager::StyleImage &img = *item.image; const FontManager::StyleImage &img = *item.image;
ImVec2 select_size(0.f, std::max(img.tex_size.y, m_gui_cfg->min_style_image_height)); ImVec2 select_size(0.f, std::max(img.tex_size.y, m_gui_cfg->min_style_image_height));
if (ImGui::Selectable("##style_select", is_selected, flags, select_size)) { if (ImGui::Selectable("##style_select", is_selected, flags, select_size)) {
if (m_font_manager.load_font(index)) process(); if (m_font_manager.load_font(index)) {
process();
select_stored_font_item();
}
} else if (ImGui::IsItemHovered()) } else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", actual_style_name.c_str()); ImGui::SetTooltip("%s", actual_style_name.c_str());
@ -991,9 +1017,10 @@ void GLGizmoEmboss::draw_style_list() {
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Duplicate style.").c_str()); ImGui::SetTooltip("%s", _u8L("Duplicate style.").c_str());
// TODO: Is style changed against stored one // TODO: Is style changed against stored one
bool is_changed = false; bool is_stored = m_stored_font_item.has_value();
bool is_changed = (is_stored) ?
!(*m_stored_font_item == m_font_manager.get_font_item()) : true;
ImGui::SameLine(); ImGui::SameLine();
if (draw_button(IconType::save, !is_changed)) { if (draw_button(IconType::save, !is_changed)) {
@ -1025,7 +1052,10 @@ void GLGizmoEmboss::draw_style_list() {
m_font_manager.add_fonts(font_list); m_font_manager.add_fonts(font_list);
// TODO: What to do when default fonts are not loadable? // TODO: What to do when default fonts are not loadable?
bool success = m_font_manager.load_first_valid_font(); bool success = m_font_manager.load_first_valid_font();
select_stored_font_item();
} }
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Revert all styles").c_str());
#endif // ALLOW_REVERT_ALL_STYLES #endif // ALLOW_REVERT_ALL_STYLES
} }

View File

@ -18,6 +18,7 @@
#include "libslic3r/Emboss.hpp" #include "libslic3r/Emboss.hpp"
#include "libslic3r/Point.hpp" #include "libslic3r/Point.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/TextConfiguration.hpp"
#include <imgui/imgui.h> #include <imgui/imgui.h>
@ -148,6 +149,9 @@ private:
std::string depth; std::string depth;
}; };
Translations translations; Translations translations;
std::map<std::string, FontItem> default_styles;
GuiCfg() = default; GuiCfg() = default;
}; };
std::optional<const GuiCfg> m_gui_cfg; std::optional<const GuiCfg> m_gui_cfg;
@ -157,7 +161,8 @@ private:
bool m_is_advanced_edit_style = false; bool m_is_advanced_edit_style = false;
FontManager m_font_manager; FontManager m_font_manager;
std::optional<FontItem> m_stored_font_item;
void select_stored_font_item();
//FontList m_font_list; //FontList m_font_list;
//size_t m_font_selected;// index to m_font_list //size_t m_font_selected;// index to m_font_list

View File

@ -2,7 +2,6 @@
#include <libslic3r/Model.hpp> #include <libslic3r/Model.hpp>
#include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh #include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh
#include <libslic3r/BuildVolume.hpp>
#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/NotificationManager.hpp"
@ -102,15 +101,13 @@ void EmbossCreateJob::process(Ctl &ctl) {
if (m_result.its.empty()) m_result = create_default_mesh(); if (m_result.its.empty()) m_result = create_default_mesh();
if (ctl.was_canceled()) return; if (ctl.was_canceled()) return;
Plater * plater = wxGetApp().plater(); // may be move to input
std::optional<RaycastManager::Hit> hit; std::optional<RaycastManager::Hit> hit;
if (m_input->object_idx.has_value()) { if (m_input->object_idx.has_value()) {
// By position of cursor create transformation to put text on surface of model // By position of cursor create transformation to put text on surface of model
const ModelObjectPtrs &objects = wxGetApp().plater()->model().objects; ModelObject *obj = wxGetApp().plater()->model().objects[*m_input->object_idx];
m_input->raycast_manager->actualize(objects); m_input->raycast_manager->actualize(obj);
if (ctl.was_canceled()) return; if (ctl.was_canceled()) return;
hit = m_input->raycast_manager->unproject(m_input->screen_coor); hit = m_input->raycast_manager->unproject(m_input->screen_coor, m_input->camera);
// context menu for add text could be open only by right click on an // context menu for add text could be open only by right click on an
// object. After right click, object is selected and object_idx is set // object. After right click, object is selected and object_idx is set
@ -123,14 +120,12 @@ void EmbossCreateJob::process(Ctl &ctl) {
// create new object // create new object
// calculate X,Y offset position for lay on platter in place of // calculate X,Y offset position for lay on platter in place of
// mouse click // mouse click
const Camera &camera = plater->get_camera(); Vec2d bed_coor = CameraUtils::get_z0_position(m_input->camera, m_input->screen_coor);
Vec2d bed_coor = CameraUtils::get_z0_position(camera, m_input->screen_coor);
// check point is on build plate: // check point is on build plate:
Pointfs bed_shape = plater->build_volume().bed_shape();
Points bed_shape_; Points bed_shape_;
bed_shape_.reserve(bed_shape.size()); bed_shape_.reserve(m_input->bed_shape.size());
for (const Vec2d &p : bed_shape) for (const Vec2d &p : m_input->bed_shape)
bed_shape_.emplace_back(p.cast<int>()); bed_shape_.emplace_back(p.cast<int>());
Polygon bed(bed_shape_); Polygon bed(bed_shape_);
if (!bed.contains(bed_coor.cast<int>())) if (!bed.contains(bed_coor.cast<int>()))
@ -143,8 +138,13 @@ void EmbossCreateJob::process(Ctl &ctl) {
Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z()); Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z());
m_transformation = Transform3d(tt); m_transformation = Transform3d(tt);
} else { } else {
m_transformation = Emboss::create_transformation_onto_surface( // TODO: Disable apply common transformation after draggig
hit->position, hit->normal); // Call after is used for apply transformation after common dragging to rewrite it
m_transformation = Emboss::create_transformation_onto_surface(hit->position, hit->normal);
if (m_input->hit_vol_tr.has_value()) {
Transform3d object_trmat = m_input->raycast_manager->get_transformation(hit->tr_key);
m_transformation = m_input->hit_vol_tr->inverse() * object_trmat * m_transformation;
}
} }
} }

View File

@ -4,6 +4,7 @@
#include <libslic3r/Emboss.hpp> #include <libslic3r/Emboss.hpp>
#include <libslic3r/ModelVolumeType.hpp> #include <libslic3r/ModelVolumeType.hpp>
#include "slic3r/Utils/RaycastManager.hpp" #include "slic3r/Utils/RaycastManager.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "Job.hpp" #include "Job.hpp"
namespace Slic3r { namespace Slic3r {
@ -110,6 +111,15 @@ struct EmbossDataCreate: public EmbossDataBase
// when exist ModelObject where to create volume // when exist ModelObject where to create volume
std::optional<int> object_idx; std::optional<int> object_idx;
// hitted instance transformation
std::optional<Transform3d> hit_vol_tr;
// projection property
Camera camera;
// shape of bed in case of create volume on bed
std::vector<Vec2d> bed_shape;
// used to find point on surface where to create new object // used to find point on surface where to create new object
RaycastManager *raycast_manager; RaycastManager *raycast_manager;
// It is inside of GLGizmoEmboss object, // It is inside of GLGizmoEmboss object,
@ -121,11 +131,17 @@ struct EmbossDataCreate: public EmbossDataBase
ModelVolumeType volume_type, ModelVolumeType volume_type,
Vec2d screen_coor, Vec2d screen_coor,
std::optional<int> object_idx, std::optional<int> object_idx,
const std::optional<Transform3d>& hit_vol_tr,
const Camera& camera,
const std::vector<Vec2d> & bed_shape,
RaycastManager * raycast_manager) RaycastManager * raycast_manager)
: EmbossDataBase(std::move(font_file), text_configuration, volume_name) : EmbossDataBase(std::move(font_file), text_configuration, volume_name)
, volume_type(volume_type) , volume_type(volume_type)
, screen_coor(screen_coor) , screen_coor(screen_coor)
, object_idx(object_idx) , object_idx(object_idx)
, hit_vol_tr(hit_vol_tr)
, camera(camera)
, bed_shape(bed_shape)
, raycast_manager(raycast_manager) , raycast_manager(raycast_manager)
{} {}
}; };

View File

@ -73,8 +73,45 @@ void RaycastManager::actualize(const ModelObjectPtrs &objects,
transformations.erase(transformation_key); transformations.erase(transformation_key);
} }
void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
{
// actualize MeshRaycaster
for (const ModelVolume *volume : object->volumes) {
size_t oid = volume->id().id;
if (skip != nullptr && skip->skip(oid))
continue;
auto item = raycasters.find(oid);
if (item == raycasters.end()) {
// add new raycaster
auto raycaster = std::make_unique<MeshRaycaster>(volume->mesh());
raycasters.insert(std::make_pair(oid, std::move(raycaster)));
}
}
// actualize transformation matrices
for (const ModelVolume *volume : object->volumes) {
if (skip != nullptr && skip->skip(volume->id().id)) continue;
const Transform3d &volume_tr = volume->get_matrix();
for (const ModelInstance *instance : object->instances) {
const Transform3d &instrance_tr = instance->get_matrix();
Transform3d transformation = instrance_tr * volume_tr;
// TODO: add SLA shift Z
// transformation.translation()(2) += m_sla_shift_z;
TrKey tr_key = std::make_pair(instance->id().id, volume->id().id);
auto item = transformations.find(tr_key);
if (item != transformations.end()) {
// actualize transformation all the time
item->second = transformation;
} else {
// add new transformation
transformations.insert(std::make_pair(tr_key, transformation));
}
}
}
}
std::optional<RaycastManager::Hit> RaycastManager::unproject( std::optional<RaycastManager::Hit> RaycastManager::unproject(
const Vec2d &mouse_pos, const ISkip *skip) const const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
{ {
struct HitWithDistance: public Hit struct HitWithDistance: public Hit
{ {
@ -87,8 +124,6 @@ std::optional<RaycastManager::Hit> RaycastManager::unproject(
{} {}
}; };
std::optional<HitWithDistance> closest; std::optional<HitWithDistance> closest;
const Camera &camera = wxGetApp().plater()->get_camera();
for (const auto &item : transformations) { for (const auto &item : transformations) {
const TrKey &key = item.first; const TrKey &key = item.first;
size_t volume_id = key.second; size_t volume_id = key.second;

View File

@ -53,6 +53,8 @@ public:
void actualize(const ModelObjectPtrs &objects, void actualize(const ModelObjectPtrs &objects,
const ISkip * skip = nullptr); const ISkip * skip = nullptr);
void actualize(const ModelObject *object, const ISkip *skip = nullptr);
// TODO: it is more general object move outside of this class // TODO: it is more general object move outside of this class
struct SurfacePoint struct SurfacePoint
{ {
@ -85,9 +87,11 @@ public:
/// Note: Function use current camera position from wxGetApp() /// Note: Function use current camera position from wxGetApp()
/// </summary> /// </summary>
/// <param name="mouse_pos">Position of mouse on screen</param> /// <param name="mouse_pos">Position of mouse on screen</param>
/// <param name="camera">Projection params</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param> /// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns>Position on surface, normal direction and transformation key, which define hitted object instance</returns> /// <returns>Position on surface, normal direction and transformation key, which define hitted object instance</returns>
std::optional<Hit> unproject(const Vec2d &mouse_pos, std::optional<Hit> unproject(const Vec2d &mouse_pos,
const Camera &camera,
const ISkip *skip = nullptr) const; const ISkip *skip = nullptr) const;
/// <summary> /// <summary>