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
};
bool operator==(const FontItem &other) const {
return
type == other.type &&
prop == other.prop &&
name == other.name &&
path == other.path
;
}
// undo / redo stack recovery
template<class Archive> void serialize(Archive &ar)
{

View file

@ -24,6 +24,7 @@
#include "libslic3r/AppConfig.hpp" // store/load font list
#include "libslic3r/MapUtils.hpp"
#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 "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<Transform3d> hit_vol_tr;
const Selection &selection = m_parent.get_selection();
if (!selection.is_empty()) object_idx = selection.get_object_idx();
auto data =
std::make_unique<EmbossDataCreate>(m_font_manager.get_font_file(),
create_configuration(),
create_volume_name(), volume_type,
screen_coor, object_idx,
&m_raycast_manager);
auto &worker = wxGetApp().plater()->get_ui_job_worker();
if (!selection.is_empty()) {
object_idx = selection.get_object_idx();
int hovered_id = m_parent.get_first_hover_volume_idx();
if (hovered_id >= 0) {
GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id];
hit_vol_tr = gl_volume->get_instance_transformation().get_matrix();
}
}
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)));
}
@ -206,14 +220,15 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
// initialize raycasters
// IMPROVE: move to job, for big scene it slows down
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;
}
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
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()) {
// there is no hit
// 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)
{
initialize();
check_selection();
check_selection();
// TODO: fix width - showing scroll in first draw of advanced.
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 -
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);
// 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;
FontList font_list = load_font_list_from_app_config(app_cfg);
m_font_manager.add_fonts(font_list);
// TODO: select last session sellected font index
if (!m_font_manager.load_first_valid_font()) {
FontList font_list = create_default_font_list();
m_font_manager.add_fonts(font_list);
m_font_manager.add_fonts(default_font_list);
// TODO: What to do when default fonts are not loadable?
bool success = m_font_manager.load_first_valid_font();
assert(success);
}
set_default_text();
select_stored_font_item();
}
FontList GLGizmoEmboss::create_default_font_list()
{
// https://docs.wxwidgets.org/3.0/classwx_font.html
// 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 {
par_fi,
perp_fi,
fix_fi,
negative_fi,
WxFontUtils::get_font_item(*wxNORMAL_FONT), // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)
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_font_item(*wxNORMAL_FONT, _u8L("NORMAL")), // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)
WxFontUtils::get_font_item(*wxSMALL_FONT, _u8L("SMALL")), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT.
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.
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(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), _u8L("MODERN"))
//, WxFontUtils::get_os_font() == wxNORMAL_FONT
};
}
@ -617,6 +627,18 @@ void GLGizmoEmboss::close()
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()
{
#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.free_style_images();
ImGui::CloseCurrentPopup();
select_stored_font_item();
}
ImGui::EndPopup();
}
@ -936,7 +959,10 @@ void GLGizmoEmboss::draw_style_list() {
const FontManager::StyleImage &img = *item.image;
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 (m_font_manager.load_font(index)) process();
if (m_font_manager.load_font(index)) {
process();
select_stored_font_item();
}
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", actual_style_name.c_str());
@ -991,9 +1017,10 @@ void GLGizmoEmboss::draw_style_list() {
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Duplicate style.").c_str());
// 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();
if (draw_button(IconType::save, !is_changed)) {
@ -1025,7 +1052,10 @@ void GLGizmoEmboss::draw_style_list() {
m_font_manager.add_fonts(font_list);
// TODO: What to do when default fonts are not loadable?
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
}

View file

@ -18,6 +18,7 @@
#include "libslic3r/Emboss.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TextConfiguration.hpp"
#include <imgui/imgui.h>
@ -148,6 +149,9 @@ private:
std::string depth;
};
Translations translations;
std::map<std::string, FontItem> default_styles;
GuiCfg() = default;
};
std::optional<const GuiCfg> m_gui_cfg;
@ -157,7 +161,8 @@ private:
bool m_is_advanced_edit_style = false;
FontManager m_font_manager;
std::optional<FontItem> m_stored_font_item;
void select_stored_font_item();
//FontList 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/Format/OBJ.hpp> // load_obj for default mesh
#include <libslic3r/BuildVolume.hpp>
#include "slic3r/GUI/Plater.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 (ctl.was_canceled()) return;
Plater * plater = wxGetApp().plater(); // may be move to input
std::optional<RaycastManager::Hit> hit;
if (m_input->object_idx.has_value()) {
// By position of cursor create transformation to put text on surface of model
const ModelObjectPtrs &objects = wxGetApp().plater()->model().objects;
m_input->raycast_manager->actualize(objects);
ModelObject *obj = wxGetApp().plater()->model().objects[*m_input->object_idx];
m_input->raycast_manager->actualize(obj);
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
// object. After right click, object is selected and object_idx is set
@ -123,14 +120,12 @@ void EmbossCreateJob::process(Ctl &ctl) {
// create new object
// calculate X,Y offset position for lay on platter in place of
// mouse click
const Camera &camera = plater->get_camera();
Vec2d bed_coor = CameraUtils::get_z0_position(camera, m_input->screen_coor);
Vec2d bed_coor = CameraUtils::get_z0_position(m_input->camera, m_input->screen_coor);
// check point is on build plate:
Pointfs bed_shape = plater->build_volume().bed_shape();
Points bed_shape_;
bed_shape_.reserve(bed_shape.size());
for (const Vec2d &p : bed_shape)
bed_shape_.reserve(m_input->bed_shape.size());
for (const Vec2d &p : m_input->bed_shape)
bed_shape_.emplace_back(p.cast<int>());
Polygon bed(bed_shape_);
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());
m_transformation = Transform3d(tt);
} else {
m_transformation = Emboss::create_transformation_onto_surface(
hit->position, hit->normal);
// TODO: Disable apply common transformation after draggig
// 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/ModelVolumeType.hpp>
#include "slic3r/Utils/RaycastManager.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "Job.hpp"
namespace Slic3r {
@ -110,6 +111,15 @@ struct EmbossDataCreate: public EmbossDataBase
// when exist ModelObject where to create volume
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
RaycastManager *raycast_manager;
// It is inside of GLGizmoEmboss object,
@ -121,11 +131,17 @@ struct EmbossDataCreate: public EmbossDataBase
ModelVolumeType volume_type,
Vec2d screen_coor,
std::optional<int> object_idx,
const std::optional<Transform3d>& hit_vol_tr,
const Camera& camera,
const std::vector<Vec2d> & bed_shape,
RaycastManager * raycast_manager)
: EmbossDataBase(std::move(font_file), text_configuration, volume_name)
, volume_type(volume_type)
, screen_coor(screen_coor)
, object_idx(object_idx)
, hit_vol_tr(hit_vol_tr)
, camera(camera)
, bed_shape(bed_shape)
, raycast_manager(raycast_manager)
{}
};

View file

@ -73,8 +73,45 @@ void RaycastManager::actualize(const ModelObjectPtrs &objects,
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(
const Vec2d &mouse_pos, const ISkip *skip) const
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
{
struct HitWithDistance: public Hit
{
@ -87,8 +124,6 @@ std::optional<RaycastManager::Hit> RaycastManager::unproject(
{}
};
std::optional<HitWithDistance> closest;
const Camera &camera = wxGetApp().plater()->get_camera();
for (const auto &item : transformations) {
const TrKey &key = item.first;
size_t volume_id = key.second;

View file

@ -53,6 +53,8 @@ public:
void actualize(const ModelObjectPtrs &objects,
const ISkip * skip = nullptr);
void actualize(const ModelObject *object, const ISkip *skip = nullptr);
// TODO: it is more general object move outside of this class
struct SurfacePoint
{
@ -85,9 +87,11 @@ public:
/// Note: Function use current camera position from wxGetApp()
/// </summary>
/// <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>
/// <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;
/// <summary>