Emboss SVG shape

This commit is contained in:
Filip Sykala - NTB T15p 2023-04-18 13:44:31 +02:00
parent b3d2dbeeb3
commit 51441bc5f0
7 changed files with 274 additions and 235 deletions

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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
////////////////////////////

View File

@ -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_

View File

@ -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