mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-25 04:24:25 +08:00
Emboss SVG shape
This commit is contained in:
parent
b3d2dbeeb3
commit
51441bc5f0
@ -3508,6 +3508,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
wxGetApp().obj_list()->update_selections();
|
||||
return;
|
||||
} else if (hover_volume->emboss_shape.has_value()) {
|
||||
m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id});
|
||||
if (type != GLGizmosManager::EType::Svg)
|
||||
m_gizmos.open_gizmo(GLGizmosManager::EType::Svg);
|
||||
wxGetApp().obj_list()->update_selections();
|
||||
|
@ -62,7 +62,6 @@
|
||||
#define SHOW_FINE_POSITION // draw convex hull around volume
|
||||
#define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position
|
||||
#define ALLOW_OPEN_NEAR_VOLUME
|
||||
#define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread
|
||||
#endif // ALLOW_DEBUG_MODE
|
||||
|
||||
//#define USE_PIXEL_SIZE_IN_WX_FONT
|
||||
@ -797,16 +796,10 @@ void GLGizmoEmboss::on_set_state()
|
||||
// change position of just opened emboss window
|
||||
if (m_allow_open_near_volume) {
|
||||
m_set_window_offset = calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size());
|
||||
} else {
|
||||
if (m_gui_cfg != nullptr)
|
||||
m_set_window_offset = ImGuiWrapper::change_window_position(on_get_name().c_str(), false);
|
||||
else
|
||||
m_set_window_offset = ImVec2(-1, -1);
|
||||
} else {
|
||||
m_set_window_offset = (m_gui_cfg != nullptr) ?
|
||||
ImGuiWrapper::change_window_position(on_get_name().c_str(), false) : ImVec2(-1, -1);
|
||||
}
|
||||
|
||||
// when open by hyperlink it needs to show up
|
||||
// or after key 'T' windows doesn't appear
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1110,29 +1103,6 @@ void GLGizmoEmboss::calculate_scale() {
|
||||
m_style_manager.clear_imgui_font();
|
||||
}
|
||||
|
||||
#ifdef EXECUTE_PROCESS_ON_MAIN_THREAD
|
||||
namespace priv {
|
||||
// Run Job on main thread (blocking) - ONLY DEBUG
|
||||
static inline void execute_job(std::shared_ptr<Job> j)
|
||||
{
|
||||
struct MyCtl : public Job::Ctl
|
||||
{
|
||||
void update_status(int st, const std::string &msg = "") override{};
|
||||
bool was_canceled() const override { return false; }
|
||||
std::future<void> call_on_main_thread(std::function<void()> fn) override
|
||||
{
|
||||
return std::future<void>{};
|
||||
}
|
||||
} ctl;
|
||||
j->process(ctl);
|
||||
wxGetApp().plater()->CallAfter([j]() {
|
||||
std::exception_ptr e_ptr = nullptr;
|
||||
j->finalize(false, e_ptr);
|
||||
});
|
||||
}
|
||||
} // namespace priv
|
||||
#endif
|
||||
|
||||
bool GLGizmoEmboss::process()
|
||||
{
|
||||
// no volume is selected -> selection from right panel
|
||||
@ -1151,50 +1121,12 @@ bool GLGizmoEmboss::process()
|
||||
if (!m_volume->emboss_shape.has_value()) return false;
|
||||
|
||||
DataUpdate data{create_emboss_data_base(m_text, m_style_manager, m_job_cancel), m_volume->id()};
|
||||
std::unique_ptr<Job> job = nullptr;
|
||||
bool start = start_update_volume(std::move(data), *m_volume, m_parent.get_selection(), m_raycast_manager);
|
||||
if (start)
|
||||
// notification is removed befor object is changed by job
|
||||
remove_notification_not_valid_font();
|
||||
|
||||
// check cutting from source mesh
|
||||
bool &use_surface = data.base->shape.projection.use_surface;
|
||||
if (use_surface && m_volume->is_the_only_one_part())
|
||||
use_surface = false;
|
||||
|
||||
if (use_surface) {
|
||||
// Model to cut surface from.
|
||||
SurfaceVolumeData::ModelSources sources = create_volume_sources(*m_volume);
|
||||
if (sources.empty()) return false;
|
||||
|
||||
Transform3d text_tr = m_volume->get_matrix();
|
||||
auto& fix_3mf = m_volume->emboss_shape->fix_3mf_tr;
|
||||
if (fix_3mf.has_value())
|
||||
text_tr = text_tr * fix_3mf->inverse();
|
||||
|
||||
// when it is new applying of use surface than move origin onto surfaca
|
||||
if (!m_volume->emboss_shape->projection.use_surface) {
|
||||
auto offset = calc_surface_offset(m_parent.get_selection(), m_raycast_manager);
|
||||
if (offset.has_value())
|
||||
text_tr *= Eigen::Translation<double, 3>(*offset);
|
||||
}
|
||||
|
||||
bool is_outside = m_volume->is_model_part();
|
||||
// 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)}};
|
||||
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
|
||||
} else {
|
||||
job = std::make_unique<UpdateJob>(std::move(data));
|
||||
}
|
||||
|
||||
#ifndef EXECUTE_PROCESS_ON_MAIN_THREAD
|
||||
auto &worker = wxGetApp().plater()->get_ui_job_worker();
|
||||
queue_job(worker, std::move(job));
|
||||
#else
|
||||
// Run Job on main thread (blocking) - ONLY DEBUG
|
||||
priv::execute_job(std::move(job));
|
||||
#endif // EXECUTE_PROCESS_ON_MAIN_THREAD
|
||||
|
||||
// notification is removed befor object is changed by job
|
||||
remove_notification_not_valid_font();
|
||||
return true;
|
||||
return start;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -2475,38 +2407,6 @@ bool GLGizmoEmboss::rev_slider(const std::string &name,
|
||||
undo_tooltip, undo_offset, draw_slider_float);
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::do_translate(const Vec3d &relative_move)
|
||||
{
|
||||
assert(m_volume != nullptr);
|
||||
assert(m_volume->text_configuration.has_value());
|
||||
Selection &selection = m_parent.get_selection();
|
||||
assert(!selection.is_empty());
|
||||
selection.setup_cache();
|
||||
selection.translate(relative_move, TransformationType::Local);
|
||||
|
||||
std::string snapshot_name; // empty mean no store undo / redo
|
||||
// NOTE: it use L instead of _L macro because prefix _ is appended inside
|
||||
// function do_move
|
||||
// snapshot_name = L("Set surface distance");
|
||||
m_parent.do_move(snapshot_name);
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::do_rotate(float relative_z_angle)
|
||||
{
|
||||
assert(m_volume != nullptr);
|
||||
assert(m_volume->text_configuration.has_value());
|
||||
Selection &selection = m_parent.get_selection();
|
||||
assert(!selection.is_empty());
|
||||
selection.setup_cache();
|
||||
selection.rotate(Vec3d(0., 0., relative_z_angle), get_transformation_type(selection));
|
||||
|
||||
std::string snapshot_name; // empty meand no store undo / redo
|
||||
// NOTE: it use L instead of _L macro because prefix _ is appended
|
||||
// inside function do_move
|
||||
// snapshot_name = L("Set text rotation");
|
||||
m_parent.do_rotate(snapshot_name);
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::draw_advanced()
|
||||
{
|
||||
const auto &ff = m_style_manager.get_font_file_with_cache();
|
||||
|
@ -35,15 +35,9 @@ using namespace Slic3r::Emboss;
|
||||
using namespace Slic3r::GUI;
|
||||
using namespace Slic3r::GUI::Emboss;
|
||||
|
||||
namespace priv {
|
||||
|
||||
} // namespace priv
|
||||
|
||||
GLGizmoSVG::GLGizmoSVG(GLCanvas3D &parent)
|
||||
: GLGizmoBase(parent, M_ICON_FILENAME, -3)
|
||||
, m_volume(nullptr)
|
||||
, m_rotate_gizmo(parent, GLGizmoRotate::Axis::Z) // grab id = 2 (Z axis)
|
||||
, m_job_cancel(nullptr)
|
||||
{
|
||||
m_rotate_gizmo.set_group_id(0);
|
||||
m_rotate_gizmo.set_force_local_coordinate(true);
|
||||
@ -111,8 +105,39 @@ std::string volume_name(const EmbossShape& shape);
|
||||
/// <param name="volume_type">Type of volume to be created</param>
|
||||
/// <returns>Params</returns>
|
||||
CreateVolumeParams create_input(GLCanvas3D &canvas, RaycastManager &raycaster, ModelVolumeType volume_type);
|
||||
|
||||
// This configs holds GUI layout size given by translated texts.
|
||||
// etc. When language changes, GUI is recreated and this class constructed again,
|
||||
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
|
||||
struct GuiCfg
|
||||
{
|
||||
// Detect invalid config values when change monitor DPI
|
||||
double screen_scale;
|
||||
float main_toolbar_height;
|
||||
|
||||
// Zero means it is calculated in init function
|
||||
ImVec2 minimal_window_size = ImVec2(0, 0);
|
||||
|
||||
float input_width = 0.f;
|
||||
float input_offset = 0.f;
|
||||
|
||||
float icon_width = 0.f;
|
||||
// Only translations needed for calc GUI size
|
||||
struct Translations
|
||||
{
|
||||
std::string depth;
|
||||
std::string use_surface;
|
||||
std::string rotation;
|
||||
std::string distance; // from surface
|
||||
};
|
||||
Translations translations;
|
||||
};
|
||||
GuiCfg create_gui_configuration();
|
||||
|
||||
} // namespace
|
||||
|
||||
// use private definition
|
||||
struct GLGizmoSVG::GuiCfg: public ::GuiCfg{};
|
||||
|
||||
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos)
|
||||
{
|
||||
@ -150,9 +175,8 @@ bool GLGizmoSVG::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
|
||||
if (!m_dragging) return used;
|
||||
|
||||
if (mouse_event.Dragging()) {
|
||||
auto &angle_opt = m_volume->text_configuration->style.prop.angle;
|
||||
if (!m_rotate_start_angle.has_value())
|
||||
m_rotate_start_angle = angle_opt.has_value() ? *angle_opt : 0.f;
|
||||
m_rotate_start_angle = m_angle.value_or(0.f);
|
||||
double angle = m_rotate_gizmo.get_angle();
|
||||
angle -= PI / 2; // Grabber is upward
|
||||
|
||||
@ -165,11 +189,11 @@ bool GLGizmoSVG::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
|
||||
// move to range <-M_PI, M_PI>
|
||||
Geometry::to_range_pi_pi(angle);
|
||||
// propagate angle into property
|
||||
angle_opt = static_cast<float>(angle);
|
||||
m_angle = static_cast<float>(angle);
|
||||
|
||||
// do not store zero
|
||||
if (is_approx(*angle_opt, 0.f))
|
||||
angle_opt.reset();
|
||||
if (is_approx(*m_angle, 0.f))
|
||||
m_angle.reset();
|
||||
}
|
||||
return used;
|
||||
}
|
||||
@ -192,7 +216,7 @@ bool GLGizmoSVG::on_mouse_for_translate(const wxMouseEvent &mouse_event)
|
||||
// End with surface dragging?
|
||||
if (was_dragging && !is_dragging) {
|
||||
// Update surface by new position
|
||||
if (m_volume->text_configuration->style.prop.use_surface)
|
||||
if (m_volume->emboss_shape->projection.use_surface)
|
||||
process();
|
||||
|
||||
// Show correct value of height & depth inside of inputs
|
||||
@ -226,7 +250,7 @@ bool GLGizmoSVG::on_mouse(const wxMouseEvent &mouse_event)
|
||||
// not selected volume
|
||||
if (m_volume == nullptr ||
|
||||
get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr ||
|
||||
!m_volume->text_configuration.has_value()) return false;
|
||||
!m_volume->emboss_shape.has_value()) return false;
|
||||
|
||||
if (on_mouse_for_rotation(mouse_event)) return true;
|
||||
if (on_mouse_for_translate(mouse_event)) return true;
|
||||
@ -244,10 +268,6 @@ bool GLGizmoSVG::on_init()
|
||||
m_rotate_gizmo.init();
|
||||
ColorRGBA gray_color(.6f, .6f, .6f, .3f);
|
||||
m_rotate_gizmo.set_highlight_color(gray_color);
|
||||
|
||||
// No shortCut
|
||||
// m_shortcut_key = WXK_CONTROL_T;
|
||||
|
||||
// Set rotation gizmo upwardrotate
|
||||
m_rotate_gizmo.set_angle(PI / 2);
|
||||
return true;
|
||||
@ -292,15 +312,18 @@ void GLGizmoSVG::on_render_input_window(float x, float y, float bottom_limit)
|
||||
// Configuration creation
|
||||
double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor();
|
||||
float main_toolbar_height = m_parent.get_main_toolbar_height();
|
||||
if (!m_gui_cfg.has_value() || // Exist configuration - first run
|
||||
if (m_gui_cfg == nullptr || // Exist configuration - first run
|
||||
m_gui_cfg->screen_scale != screen_scale || // change of DPI
|
||||
m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port
|
||||
) {
|
||||
// Create cache for gui offsets
|
||||
GuiCfg cfg = create_gui_configuration();
|
||||
::GuiCfg cfg = create_gui_configuration();
|
||||
cfg.screen_scale = screen_scale;
|
||||
cfg.main_toolbar_height = main_toolbar_height;
|
||||
m_gui_cfg.emplace(std::move(cfg));
|
||||
|
||||
GuiCfg gui_cfg{std::move(cfg)};
|
||||
m_gui_cfg = std::make_unique<const GuiCfg>(std::move(gui_cfg));
|
||||
|
||||
// set position near toolbar
|
||||
m_set_window_offset = ImVec2(-1.f, -1.f);
|
||||
}
|
||||
@ -358,29 +381,13 @@ void GLGizmoSVG::on_set_state()
|
||||
|
||||
// Closing gizmo. e.g. selecting another one
|
||||
if (GLGizmoBase::m_state == GLGizmoBase::Off) {
|
||||
// refuse outgoing during text preview
|
||||
if (false) {
|
||||
GLGizmoBase::m_state = GLGizmoBase::On;
|
||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||
notification_manager->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
_u8L("ERROR: Wait until ends or Cancel process."));
|
||||
return;
|
||||
}
|
||||
reset_volume();
|
||||
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
|
||||
// Try(when exist) set text configuration by volume
|
||||
set_volume_by_selection();
|
||||
|
||||
if (!m_gui_cfg.has_value())
|
||||
m_set_window_offset = ImGuiWrapper::change_window_position(on_get_name().c_str(), false);
|
||||
else
|
||||
m_set_window_offset = ImVec2(-1, -1);
|
||||
|
||||
// when open by hyperlink it needs to show up
|
||||
// or after key 'T' windows doesn't appear
|
||||
// m_parent.set_as_dirty();
|
||||
|
||||
m_set_window_offset = (m_gui_cfg != nullptr) ?
|
||||
ImGuiWrapper::change_window_position(on_get_name().c_str(), false) : ImVec2(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,14 +420,6 @@ void GLGizmoSVG::on_stop_dragging()
|
||||
}
|
||||
void GLGizmoSVG::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); }
|
||||
|
||||
GLGizmoSVG::GuiCfg GLGizmoSVG::create_gui_configuration()
|
||||
{
|
||||
GuiCfg cfg; // initialize by default values;
|
||||
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
void GLGizmoSVG::set_volume_by_selection()
|
||||
{
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
@ -454,6 +453,7 @@ void GLGizmoSVG::set_volume_by_selection()
|
||||
|
||||
m_volume = volume;
|
||||
m_volume_id = volume->id();
|
||||
m_volume_shape = *volume->emboss_shape; // copy
|
||||
|
||||
// Calculate current angle of up vector
|
||||
m_angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
|
||||
@ -469,6 +469,7 @@ void GLGizmoSVG::reset_volume()
|
||||
|
||||
m_volume = nullptr;
|
||||
m_volume_id.id = 0;
|
||||
m_volume_shape.shapes.clear();
|
||||
}
|
||||
|
||||
void GLGizmoSVG::calculate_scale() {
|
||||
@ -496,51 +497,25 @@ bool GLGizmoSVG::process()
|
||||
{
|
||||
// no volume is selected -> selection from right panel
|
||||
assert(m_volume != nullptr);
|
||||
if (m_volume == nullptr) return false;
|
||||
|
||||
//DataUpdate data{create_emboss_data_base(m_text, m_style_manager, m_job_cancel), m_volume->id()};
|
||||
if (m_volume == nullptr)
|
||||
return false;
|
||||
|
||||
assert(m_volume->emboss_shape.has_value());
|
||||
if (!m_volume->emboss_shape.has_value())
|
||||
return false;
|
||||
|
||||
//std::unique_ptr<Job> job = nullptr;
|
||||
// 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
|
||||
if (m_job_cancel != nullptr)
|
||||
m_job_cancel->store(true);
|
||||
// create new shared ptr to cancel new job
|
||||
m_job_cancel = std::make_shared<std::atomic<bool>>(false);
|
||||
|
||||
//// check cutting from source mesh
|
||||
//bool &use_surface = data.text_configuration.style.prop.use_surface;
|
||||
//bool is_object = m_volume->get_object()->volumes.size() == 1;
|
||||
//if (use_surface && is_object)
|
||||
// use_surface = false;
|
||||
//
|
||||
//if (use_surface) {
|
||||
// // Model to cut surface from.
|
||||
// SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume);
|
||||
// if (sources.empty()) return false;
|
||||
|
||||
// Transform3d text_tr = m_volume->get_matrix();
|
||||
// auto& fix_3mf = m_volume->text_configuration->fix_3mf_tr;
|
||||
// if (fix_3mf.has_value())
|
||||
// text_tr = text_tr * fix_3mf->inverse();
|
||||
|
||||
// // when it is new applying of use surface than move origin onto surfaca
|
||||
// if (!m_volume->text_configuration->style.prop.use_surface) {
|
||||
// auto offset = calc_surface_offset(*m_volume, m_raycast_manager, m_parent.get_selection());
|
||||
// if (offset.has_value())
|
||||
// text_tr *= Eigen::Translation<double, 3>(*offset);
|
||||
// }
|
||||
|
||||
// bool is_outside = m_volume->is_model_part();
|
||||
// // 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)}};
|
||||
// job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
|
||||
//} else {
|
||||
// job = std::make_unique<UpdateJob>(std::move(data));
|
||||
//}
|
||||
|
||||
//auto &worker = wxGetApp().plater()->get_ui_job_worker();
|
||||
//queue_job(worker, std::move(job));
|
||||
|
||||
//// notification is removed befor object is changed by job
|
||||
//remove_notification_not_valid_font();
|
||||
return true;
|
||||
EmbossShape shape = m_volume_shape; // copy
|
||||
auto base = std::make_unique<DataBase>(m_volume->name, m_job_cancel, std::move(shape));
|
||||
DataUpdate data{std::move(base), m_volume_id};
|
||||
return start_update_volume(std::move(data), *m_volume, m_parent.get_selection(), m_raycast_manager);
|
||||
}
|
||||
|
||||
void GLGizmoSVG::close()
|
||||
@ -555,18 +530,54 @@ void GLGizmoSVG::draw_window()
|
||||
{
|
||||
if (m_volume != nullptr && m_volume->emboss_shape.has_value())
|
||||
ImGui::Text("SVG file path is %s", m_volume->emboss_shape->svg_file_path.c_str());
|
||||
//draw_use_surface();
|
||||
|
||||
ImGui::Indent(m_gui_cfg->icon_width);
|
||||
draw_depth();
|
||||
draw_use_surface();
|
||||
draw_distance();
|
||||
draw_rotation();
|
||||
ImGui::Unindent(m_gui_cfg->icon_width);
|
||||
|
||||
if (ImGui::Button("change file")) {
|
||||
auto data = create_emboss_data_base(m_job_cancel);
|
||||
std::string file = choose_svg_file();
|
||||
std::string file = ::choose_svg_file();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
draw_model_type();
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoSVG::draw_depth()
|
||||
{
|
||||
ImGuiWrapper::text(m_gui_cfg->translations.depth);
|
||||
ImGui::SameLine(m_gui_cfg->input_offset);
|
||||
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
|
||||
|
||||
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
|
||||
double &value = m_volume_shape.projection.depth;
|
||||
const wxString tooltip = _L("Size in emboss direction.");
|
||||
if (use_inch) {
|
||||
const char *size_format = "%.2f in";
|
||||
double value_inch = value * ObjectManipulation::mm_to_in;
|
||||
if (ImGui::InputDouble("##depth", &value_inch, 1., 10., size_format)) {
|
||||
value = value_inch * ObjectManipulation::in_to_mm;
|
||||
process();
|
||||
}
|
||||
} else {
|
||||
const char *size_format = "%.1f mm";
|
||||
if (ImGui::InputDouble("##depth", &value, 1., 10., size_format))
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoSVG::draw_use_surface()
|
||||
{
|
||||
ImGuiWrapper::text(m_gui_cfg->translations.use_surface);
|
||||
ImGui::SameLine(m_gui_cfg->input_offset);
|
||||
|
||||
if (ImGui::Checkbox("##useSurface", &m_volume_shape.projection.use_surface))
|
||||
process();
|
||||
}
|
||||
|
||||
void GLGizmoSVG::draw_distance()
|
||||
{
|
||||
@ -584,7 +595,10 @@ void GLGizmoSVG::draw_distance()
|
||||
m_imgui->disabled_begin(!allowe_surface_distance);
|
||||
ScopeGuard sg([imgui = m_imgui]() { imgui->disabled_end(); });
|
||||
|
||||
ImGuiWrapper::text(_L("distance"));
|
||||
ImGuiWrapper::text(m_gui_cfg->translations.distance);
|
||||
ImGui::SameLine(m_gui_cfg->input_offset);
|
||||
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
|
||||
|
||||
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
|
||||
const wxString move_tooltip = _L("Distance of the center of the text to the model surface.");
|
||||
bool is_moved = false;
|
||||
@ -606,13 +620,16 @@ void GLGizmoSVG::draw_distance()
|
||||
is_moved = true;
|
||||
}
|
||||
|
||||
float undo_offset = ImGui::GetStyle().FramePadding.x;
|
||||
ImGui::SameLine(undo_offset);
|
||||
m_imgui->disabled_begin(!m_distance.has_value() && allowe_surface_distance);
|
||||
ScopeGuard sg2([imgui = m_imgui]() { imgui->disabled_end(); });
|
||||
|
||||
float reset_offset = ImGui::GetStyle().FramePadding.x;
|
||||
ImGui::SameLine(reset_offset);
|
||||
if (ImGui::Button("R##distance_reset")){
|
||||
m_distance.reset();
|
||||
is_moved = true;
|
||||
} else if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", _u8L("Reset distance to zero value"));
|
||||
ImGui::SetTooltip("%s", _u8L("Reset distance to zero value").c_str());
|
||||
|
||||
if (is_moved)
|
||||
do_local_z_move(m_parent, m_distance.value_or(.0f) - prev_distance);
|
||||
@ -622,13 +639,18 @@ void GLGizmoSVG::draw_rotation()
|
||||
{
|
||||
if (m_volume == nullptr)
|
||||
return;
|
||||
|
||||
ImGuiWrapper::text(m_gui_cfg->translations.rotation);
|
||||
ImGui::SameLine(m_gui_cfg->input_offset);
|
||||
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
|
||||
|
||||
// slider for Clock-wise angle in degress
|
||||
// stored angle is optional CCW and in radians
|
||||
// Convert stored value to degress
|
||||
// minus create clock-wise roation from CCW
|
||||
float angle = m_angle.value_or(0.f);
|
||||
float angle_deg = static_cast<float>(-angle * 180 / M_PI);
|
||||
if (m_imgui->slider_float("##angle", &angle, limits.angle.min, limits.angle.max, u8"%.2f DEG", 1.f, false, _L("Rotate text Clock-wise."))){
|
||||
if (m_imgui->slider_float("##angle", &angle_deg, limits.angle.min, limits.angle.max, u8"%.2f DEG", 1.f, false, _L("Rotate text Clock-wise."))){
|
||||
// convert back to radians and CCW
|
||||
double angle_rad = -angle_deg * M_PI / 180.0;
|
||||
Geometry::to_range_pi_pi(angle_rad);
|
||||
@ -645,6 +667,22 @@ void GLGizmoSVG::draw_rotation()
|
||||
process();
|
||||
}
|
||||
|
||||
// Reset button
|
||||
m_imgui->disabled_begin(!m_angle.has_value());
|
||||
ScopeGuard sg([imgui = m_imgui]() { imgui->disabled_end(); });
|
||||
|
||||
float reset_offset = ImGui::GetStyle().FramePadding.x;
|
||||
ImGui::SameLine(reset_offset);
|
||||
if (ImGui::Button("R##angle_reset")) {
|
||||
do_local_z_rotate(m_parent, -(*m_angle));
|
||||
m_angle.reset();
|
||||
|
||||
// recalculate for surface cut
|
||||
if (m_volume->emboss_shape->projection.use_surface)
|
||||
process();
|
||||
} else if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", _u8L("Reset rotation to zero value").c_str());
|
||||
|
||||
// Keep up - lock button icon
|
||||
//ImGui::SameLine(m_gui_cfg->lock_offset);
|
||||
//const IconManager::Icon &icon = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::activable);
|
||||
@ -788,6 +826,39 @@ CreateVolumeParams create_input(GLCanvas3D &canvas, RaycastManager& raycaster, M
|
||||
plater->get_ui_job_worker(), volume_type, raycaster, gizmo, gl_volume};
|
||||
}
|
||||
|
||||
GuiCfg create_gui_configuration() {
|
||||
GuiCfg cfg; // initialize by default values;
|
||||
|
||||
float line_height = ImGui::GetTextLineHeight();
|
||||
float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
float space = line_height_with_spacing - line_height;
|
||||
|
||||
cfg.icon_width = line_height;
|
||||
|
||||
GuiCfg::Translations &tr = cfg.translations;
|
||||
|
||||
tr.depth = _u8L("Depth");
|
||||
tr.use_surface = _u8L("Use surface");
|
||||
tr.distance = _u8L("From surface");
|
||||
tr.rotation = _u8L("Rotation");
|
||||
float max_tr_width = std::max({
|
||||
ImGui::CalcTextSize(tr.depth.c_str()).x,
|
||||
ImGui::CalcTextSize(tr.use_surface.c_str()).x,
|
||||
ImGui::CalcTextSize(tr.distance.c_str()).x,
|
||||
ImGui::CalcTextSize(tr.rotation.c_str()).x,
|
||||
});
|
||||
|
||||
const ImGuiStyle &style = ImGui::GetStyle();
|
||||
cfg.input_offset = style.WindowPadding.x + max_tr_width + space + cfg.icon_width;
|
||||
|
||||
ImVec2 letter_m_size = ImGui::CalcTextSize("M");
|
||||
const float count_letter_M_in_input = 12.f;
|
||||
cfg.input_width = letter_m_size.x * count_letter_M_in_input;
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
std::string choose_svg_file()
|
||||
{
|
||||
wxWindow *parent = nullptr;
|
||||
|
@ -98,6 +98,8 @@ private:
|
||||
bool process();
|
||||
void close();
|
||||
void draw_window();
|
||||
void draw_depth();
|
||||
void draw_use_surface();
|
||||
void draw_distance();
|
||||
void draw_rotation();
|
||||
void draw_model_type();
|
||||
@ -105,41 +107,20 @@ private:
|
||||
// process mouse event
|
||||
bool on_mouse_for_rotation(const wxMouseEvent &mouse_event);
|
||||
bool on_mouse_for_translate(const wxMouseEvent &mouse_event);
|
||||
|
||||
// This configs holds GUI layout size given by translated texts.
|
||||
// etc. When language changes, GUI is recreated and this class constructed again,
|
||||
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
|
||||
struct GuiCfg
|
||||
{
|
||||
// Detect invalid config values when change monitor DPI
|
||||
double screen_scale;
|
||||
float main_toolbar_height;
|
||||
|
||||
// Zero means it is calculated in init function
|
||||
ImVec2 minimal_window_size = ImVec2(0, 0);
|
||||
|
||||
float input_width = 0.f;
|
||||
float input_offset = 0.f;
|
||||
|
||||
// Only translations needed for calc GUI size
|
||||
struct Translations
|
||||
{
|
||||
std::string font;
|
||||
};
|
||||
Translations translations;
|
||||
};
|
||||
std::optional<const GuiCfg> m_gui_cfg;
|
||||
static GuiCfg create_gui_configuration();
|
||||
|
||||
struct GuiCfg;
|
||||
std::unique_ptr<const GuiCfg> m_gui_cfg = nullptr;
|
||||
|
||||
// actual selected only one volume - with emboss data
|
||||
ModelVolume *m_volume;
|
||||
ModelVolume *m_volume = nullptr;
|
||||
EmbossShape m_volume_shape; // copy from m_volume for edit
|
||||
|
||||
// When work with undo redo stack there could be situation that
|
||||
// m_volume point to unexisting volume so One need also objectID
|
||||
ObjectID m_volume_id;
|
||||
|
||||
// cancel for previous update of volume to cancel finalize part
|
||||
std::shared_ptr<std::atomic<bool>> m_job_cancel;
|
||||
std::shared_ptr<std::atomic<bool>> m_job_cancel = nullptr;
|
||||
|
||||
// Rotation gizmo
|
||||
GLGizmoRotate m_rotate_gizmo;
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
#include "slic3r/Utils/RaycastManager.hpp"
|
||||
|
||||
// #define EXECUTE_UPDATE_ON_MAIN_THREAD // debug execution on main thread
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Emboss;
|
||||
using namespace Slic3r::GUI;
|
||||
@ -607,6 +609,74 @@ bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr
|
||||
bool try_no_coor = false;
|
||||
return ::start_create_volume_on_surface_job(input, std::move(data), coor, try_no_coor);
|
||||
}
|
||||
|
||||
#ifdef EXECUTE_UPDATE_ON_MAIN_THREAD
|
||||
namespace {
|
||||
// Run Job on main thread (blocking) - ONLY DEBUG
|
||||
static inline bool execute_job(std::shared_ptr<Job> j)
|
||||
{
|
||||
struct MyCtl : public Job::Ctl
|
||||
{
|
||||
void update_status(int st, const std::string &msg = "") override{};
|
||||
bool was_canceled() const override { return false; }
|
||||
std::future<void> call_on_main_thread(std::function<void()> fn) override { return std::future<void>{}; }
|
||||
} ctl;
|
||||
j->process(ctl);
|
||||
wxGetApp().plater()->CallAfter([j]() {
|
||||
std::exception_ptr e_ptr = nullptr;
|
||||
j->finalize(false, e_ptr);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
bool start_update_volume(DataUpdate &&data, const ModelVolume &volume, const Selection &selection, RaycastManager& raycaster)
|
||||
{
|
||||
assert(data.volume_id == volume.id());
|
||||
|
||||
// check cutting from source mesh
|
||||
bool &use_surface = data.base->shape.projection.use_surface;
|
||||
if (use_surface && volume.is_the_only_one_part())
|
||||
use_surface = false;
|
||||
|
||||
std::unique_ptr<Job> job = nullptr;
|
||||
if (use_surface) {
|
||||
// Model to cut surface from.
|
||||
SurfaceVolumeData::ModelSources sources = create_volume_sources(volume);
|
||||
if (sources.empty())
|
||||
return false;
|
||||
|
||||
Transform3d volume_tr = volume.get_matrix();
|
||||
const std::optional<Transform3d> &fix_3mf = volume.emboss_shape->fix_3mf_tr;
|
||||
if (fix_3mf.has_value())
|
||||
volume_tr = volume_tr * fix_3mf->inverse();
|
||||
|
||||
// when it is new applying of use surface than move origin onto surfaca
|
||||
if (!volume.emboss_shape->projection.use_surface) {
|
||||
auto offset = calc_surface_offset(selection, raycaster);
|
||||
if (offset.has_value())
|
||||
volume_tr *= Eigen::Translation<double, 3>(*offset);
|
||||
}
|
||||
|
||||
bool is_outside = volume.is_model_part();
|
||||
// check that there is not unexpected volume type
|
||||
assert(is_outside || volume.is_negative_volume() || volume.is_modifier());
|
||||
UpdateSurfaceVolumeData surface_data{std::move(data), {volume_tr, is_outside, std::move(sources)}};
|
||||
job = std::make_unique<UpdateSurfaceVolumeJob>(std::move(surface_data));
|
||||
} else {
|
||||
job = std::make_unique<UpdateJob>(std::move(data));
|
||||
}
|
||||
|
||||
#ifndef EXECUTE_UPDATE_ON_MAIN_THREAD
|
||||
auto &worker = wxGetApp().plater()->get_ui_job_worker();
|
||||
return queue_job(worker, std::move(job));
|
||||
#else
|
||||
// Run Job on main thread (blocking) - ONLY DEBUG
|
||||
return execute_job(std::move(job));
|
||||
#endif // EXECUTE_UPDATE_ON_MAIN_THREAD
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI::Emboss
|
||||
|
||||
////////////////////////////
|
||||
|
@ -25,6 +25,7 @@ class RaycastManager;
|
||||
class Plater;
|
||||
class GLCanvas3D;
|
||||
class Worker;
|
||||
class Selection;
|
||||
}}
|
||||
|
||||
namespace Slic3r::GUI::Emboss {
|
||||
@ -217,6 +218,17 @@ bool start_create_volume(CreateVolumeParams &input, DataBasePtr data, const Vec2
|
||||
/// Need to suggest position or put near the selection
|
||||
/// </summary>
|
||||
bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr data);
|
||||
|
||||
/// <summary>
|
||||
/// Start job for update embossed volume
|
||||
/// </summary>
|
||||
/// <param name="data">define update data</param>
|
||||
/// <param name="volume">Volume to be updated</param>
|
||||
/// <param name="selection">Keep model and gl_volumes - when start use surface volume must be selected</param>
|
||||
/// <param name="raycaster">Could cast ray to scene</param>
|
||||
/// <returns>True when start job otherwise false</returns>
|
||||
bool start_update_volume(DataUpdate &&data, const ModelVolume &volume, const Selection &selection, RaycastManager &raycaster);
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
#endif // slic3r_EmbossJob_hpp_
|
||||
|
@ -310,7 +310,7 @@ std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastMana
|
||||
auto cond = RaycastManager::SkipVolume(volume->id().id);
|
||||
raycast_manager.actualize(*instance, &cond);
|
||||
|
||||
Transform3d to_world = world_matrix_fixed(gl_volume, selection.get_model()->objects);
|
||||
Transform3d to_world = world_matrix_fixed(gl_volume, objects);
|
||||
Vec3d point = to_world * Vec3d::Zero();
|
||||
Vec3d direction = to_world.linear() * (-Vec3d::UnitZ());
|
||||
|
||||
@ -491,8 +491,12 @@ void do_local_z_rotate(GLCanvas3D &canvas, double relative_angle)
|
||||
assert(!selection.is_empty());
|
||||
if(selection.is_empty()) return;
|
||||
|
||||
assert(selection.is_single_full_object() || selection.is_single_volume());
|
||||
if (!selection.is_single_full_object() && !selection.is_single_volume()) return;
|
||||
|
||||
selection.setup_cache();
|
||||
TransformationType transformation_type = TransformationType::Local_Relative_Joint;
|
||||
TransformationType transformation_type = selection.is_single_volume() ?
|
||||
TransformationType::Local_Relative_Joint : TransformationType::Instance_Relative_Joint;
|
||||
selection.rotate(Vec3d(0., 0., relative_angle), transformation_type);
|
||||
|
||||
std::string snapshot_name; // empty meand no store undo / redo
|
||||
|
Loading…
x
Reference in New Issue
Block a user