From 9e19d9ca0670350f17d6284f56410395471491e6 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 24 Nov 2022 20:51:58 +0100 Subject: [PATCH] Create text by menu in right panel Issue 54 (right panel -Sidebar) Fix creating text volume inside deleted object. --- src/slic3r/GUI/GUI_Factories.cpp | 12 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 527 ++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 16 +- src/slic3r/GUI/Jobs/EmbossJob.cpp | 34 +- src/slic3r/GUI/Jobs/EmbossJob.hpp | 4 +- 5 files changed, 394 insertions(+), 199 deletions(-) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 8b07741ed3..0ae1b8870a 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -508,15 +508,17 @@ void MenuFactory::append_menu_item_add_text(wxMenu* menu, ModelVolumeType type, assert(emboss != nullptr); if (emboss == nullptr) return; - auto screen_position = canvas->get_popup_menu_position(); - assert(screen_position.has_value()); - if (!screen_position.has_value()) return; - ModelVolumeType volume_type = type; // no selected object means create new object if (volume_type == ModelVolumeType::INVALID) volume_type = ModelVolumeType::MODEL_PART; - emboss->create_volume(volume_type, *screen_position); + + auto screen_position = canvas->get_popup_menu_position(); + if (screen_position.has_value()) { + emboss->create_volume(volume_type, *screen_position); + } else { + emboss->create_volume(volume_type); + } }; if ( type == ModelVolumeType::MODEL_PART diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 6d763e7786..7a853e7187 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -52,7 +52,7 @@ #define SHOW_OFFSET_DURING_DRAGGING // when drag with text over surface visualize used center #define SHOW_IMGUI_ATLAS #define SHOW_ICONS_TEXTURE -#define SHOW_FINE_POSITION +#define SHOW_FINE_POSITION // draw convex hull around volume #define SHOW_WX_WEIGHT_INPUT #define DRAW_PLACE_TO_ADD_TEXT #endif // ALLOW_DEBUG_MODE @@ -140,34 +140,7 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) // paste HEX unicode into notepad move cursor after unicode press [alt] + [x] } -void GLGizmoEmboss::set_fine_position() -{ - const Selection &selection = m_parent.get_selection(); - const Selection::IndicesList indices = selection.get_volume_idxs(); - // no selected volume - if (indices.empty()) return; - const GLVolume *volume = selection.get_volume(*indices.begin()); - // bad volume selected (e.g. deleted one) - if (volume == nullptr) return; - - const Camera &camera = wxGetApp().plater()->get_camera(); - Polygon hull = CameraUtils::create_hull2d(camera, *volume); - - const ImVec2 &windows_size = get_minimal_window_size(); - Size c_size = m_parent.get_canvas_size(); - ImVec2 canvas_size(c_size.get_width(), c_size.get_height()); - ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, canvas_size); - m_set_window_offset = offset; - return; - - Polygon rect({Point(offset.x, offset.y), - Point(offset.x + windows_size.x, offset.y), - Point(offset.x + windows_size.x, offset.y + windows_size.y), - Point(offset.x, offset.y + windows_size.y)}); - ImGuiWrapper::draw(hull); - ImGuiWrapper::draw(rect); -} - +// Private namespace with helper function for create volume namespace priv { /// @@ -178,52 +151,143 @@ namespace priv { /// Base data for emboss text static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager); +static bool is_valid(ModelVolumeType volume_type); + +/// +/// Start job for add new volume to object with given transformation +/// +/// Define where to add +/// Volume transformation +/// Define text +/// Type of volume +static void start_create_volume_job(const ModelObject *object, + const Transform3d volume_trmat, + DataBase &emboss_data, + ModelVolumeType volume_type); + +static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); + +/// +/// Start job for add new volume on surface of object defined by screen coor +/// +/// Define params of text +/// Emboss / engrave +/// Mouse position which define position +/// Volume to find surface for create +/// Ability to ray cast to model +/// True when start creation, False when there is no hit surface by screen coor +static bool start_create_volume_on_surface_job(DataBase &emboss_data, + ModelVolumeType volume_type, + const Vec2d &screen_coor, + const GLVolume *gl_volume, + RaycastManager &raycaster); + +/// +/// Find volume in selected object with closest convex hull to screen center. +/// Return +/// +/// Define where to search for closest +/// Canvas center(dependent on camera settings) +/// Actual objects +/// OUT: coordinate of controid of closest volume +/// OUT: closest volume +static void find_closest_volume(const Selection &selection, + const Vec2d &screen_center, + const Camera &camera, + const ModelObjectPtrs &objects, + Vec2d *closest_center, + const GLVolume **closest_volume); + +/// +/// Start job for add object with text into scene +/// +/// Define params of text +/// Screen coordinat, where to create new object laying on bed +static void start_create_object_job(DataBase &emboss_data, const Vec2d &coor); + static void message_disable_cut_surface(){ wxMessageBox(_L("Can NOT cut surface from nothing. Function 'use surface' was disabled for this text."), _L("Disable 'use surface' from style"), wxOK | wxICON_WARNING);} +/// +/// Create transformation for new created emboss object by mouse position +/// +/// Define where to add object +/// Actual camera view +/// Define shape of bed for its center and check that coor is on bed center +/// Emboss size / 2 +/// Transformation for create text on bed +static Transform3d create_transformation_on_bed(const Vec2d &screen_coor, + const Camera &camera, + const std::vector &bed_shape, + double z); } // namespace priv +bool priv::is_valid(ModelVolumeType volume_type){ + if (volume_type == ModelVolumeType::MODEL_PART || + volume_type == ModelVolumeType::NEGATIVE_VOLUME || + volume_type == ModelVolumeType::PARAMETER_MODIFIER) + return true; + + BOOST_LOG_TRIVIAL(error) << "Can't create embossed volume with this type: " << (int)volume_type; + return false; +} + void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) { - assert(volume_type == ModelVolumeType::MODEL_PART || - volume_type == ModelVolumeType::NEGATIVE_VOLUME || - volume_type == ModelVolumeType::PARAMETER_MODIFIER); + if (!priv::is_valid(volume_type)) return; + if (!m_gui_cfg.has_value()) initialize(); + set_default_text(); + m_style_manager.discard_style_changes(); + + GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent); + DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager); + // Try to cast ray into scene and find object for add volume + if (priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager)) + // object found + return; + + // object is not under mouse position soo create object on plater + priv::start_create_object_job(emboss_data, mouse_pos); +} + +void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) +{ + if (!priv::is_valid(volume_type)) return; if (!m_gui_cfg.has_value()) initialize(); set_default_text(); m_style_manager.discard_style_changes(); - Vec2d screen_coor = mouse_pos; - if (mouse_pos.x() < 0 || mouse_pos.y() < 0) { - // use center of screen - auto screen_size = m_parent.get_canvas_size(); - screen_coor.x() = screen_size.get_width() / 2.; - screen_coor.y() = screen_size.get_height() / 2.; + // select position by camera position and view direction + const Selection &selection = m_parent.get_selection(); + int object_idx = selection.get_object_idx(); + + Size s = m_parent.get_canvas_size(); + Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.); + DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager); + if (!selection.is_empty() && object_idx >= 0) { + // create volume inside of object + const Plater &plater = *wxGetApp().plater(); + const Camera &camera = plater.get_camera(); + const ModelObjectPtrs &objects = wxGetApp().model().objects; + + Vec2d coor; + const GLVolume *vol = nullptr; + priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol); + if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) { + assert(vol != nullptr); + // in centroid of convex hull is not hit with object + // soo create transfomation on border of object + const ModelObject *obj = objects[vol->object_idx()]; + const BoundingBoxf3& bb = obj->bounding_box(); + Transform3d volume_trmat(Eigen::Translation3d(bb.max.x(), 0., 0.)); + priv::start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); + } + } else { + // create Object on center of screen + // when ray throw center of screen not hit bed it create object on center of bed + priv::start_create_object_job(emboss_data, screen_center); } - - if (start_volume_creation(volume_type, screen_coor)) return; - - // start creation of new object - Plater* plater = wxGetApp().plater(); - const Camera &camera = plater->get_camera(); - const Pointfs &bed_shape = plater->build_volume().bed_shape(); - // TODO: Fix double creation of base data (first is inside function start_volume_creation) - DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager); - FontProp& prop = emboss_data.text_configuration.style.prop; - - // can't create new object with using surface - if (prop.use_surface) { - priv::message_disable_cut_surface(); - prop.use_surface = false; - } - - // can't create new object with distance from surface - if (prop.distance.has_value()) prop.distance.reset(); - - DataCreateObject data{std::move(emboss_data), screen_coor, camera, bed_shape}; - auto job = std::make_unique(std::move(data)); - Worker &worker = plater->get_ui_job_worker(); - queue_job(worker, std::move(job)); } bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) @@ -555,7 +619,7 @@ static void draw_fine_position(const Selection &selection, const Size &canvas, const ImVec2 &windows_size) { - const Selection::IndicesList indices = selection.get_volume_idxs(); + const Selection::IndicesList& indices = selection.get_volume_idxs(); // no selected volume if (indices.empty()) return; const GLVolume *volume = selection.get_volume(*indices.begin()); @@ -835,81 +899,6 @@ EmbossStyles GLGizmoEmboss::create_default_styles() void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } -bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type, const Vec2d &screen_coor) -{ - Plater* plater = wxGetApp().plater(); - - const Selection &selection = m_parent.get_selection(); - if (selection.is_empty()) return false; - - int hovered_id_signed = m_parent.get_first_hover_volume_idx(); - if (hovered_id_signed < 0) return false; - - size_t hovered_id = static_cast(hovered_id_signed); - auto &volumes = m_parent.get_volumes().volumes; - if (hovered_id >= volumes.size()) return false; - - int object_idx_signed = selection.get_object_idx(); - if (object_idx_signed < 0) return false; - - size_t object_idx = static_cast(object_idx_signed); - auto &objects = plater->model().objects; - if (object_idx >= objects.size()) return false; - - ModelObject *obj = objects[object_idx]; - m_raycast_manager.actualize(obj); - - const Camera &camera = plater->get_camera(); - std::optional hit = - m_raycast_manager.unproject(screen_coor, 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 - // also hit must exist. But there is proper behavior when hit doesn't - // exists. When this assert appear distquish remove of it. - assert(hit.has_value()); - if (!hit.has_value()) return false; - - Transform3d hit_object_trmat = m_raycast_manager.get_transformation(hit->tr_key); - - GLVolume *gl_volume = volumes[hovered_id]; - Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix(); - - DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager); - - // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); - const FontProp& font_prop = emboss_data.text_configuration.style.prop; - apply_transformation(font_prop, surface_trmat); - Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat; - - bool &use_surface = emboss_data.text_configuration.style.prop.use_surface; - std::unique_ptr job; - if (use_surface) { - // Model to cut surface from. - SurfaceVolumeData::ModelSources sources = create_sources(obj->volumes); - if (sources.empty()) { - priv::message_disable_cut_surface(); - use_surface = false; - } else { - bool is_outside = volume_type == ModelVolumeType::MODEL_PART; - // check that there is not unexpected volume type - assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER); - CreateSurfaceVolumeData surface_data{std::move(emboss_data), volume_trmat, is_outside, - std::move(sources), volume_type, object_idx_signed}; - job = std::make_unique(std::move(surface_data)); - } - } - if (!use_surface) { - // create volume - DataCreateVolume data{std::move(emboss_data), volume_type, object_idx_signed, volume_trmat}; - job = std::make_unique(std::move(data)); - } - Worker &worker = plater->get_ui_job_worker(); - queue_job(worker, std::move(job)); - return true; -} - #include "imgui/imgui_internal.h" // to unfocus input --> ClearActiveID void GLGizmoEmboss::check_selection() { @@ -1562,6 +1551,9 @@ void GLGizmoEmboss::draw_font_preview(FaceName& face, bool is_visible) m_face_names.texture_index = texture_index; face.texture_index = texture_index; + // clear texture + + // render text to texture FontImageData data{ text, @@ -2596,6 +2588,34 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle) m_parent.do_rotate(snapshot_name); } +void GLGizmoEmboss::set_fine_position() +{ + const Selection &selection = m_parent.get_selection(); + const Selection::IndicesList indices = selection.get_volume_idxs(); + // no selected volume + if (indices.empty()) return; + const GLVolume *volume = selection.get_volume(*indices.begin()); + // bad volume selected (e.g. deleted one) + if (volume == nullptr) return; + + const Camera &camera = wxGetApp().plater()->get_camera(); + Polygon hull = CameraUtils::create_hull2d(camera, *volume); + + const ImVec2 &windows_size = get_minimal_window_size(); + Size c_size = m_parent.get_canvas_size(); + ImVec2 canvas_size(c_size.get_width(), c_size.get_height()); + ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, canvas_size); + m_set_window_offset = offset; + return; + + Polygon rect({Point(offset.x, offset.y), + Point(offset.x + windows_size.x, offset.y), + Point(offset.x + windows_size.x, offset.y + windows_size.y), + Point(offset.x, offset.y + windows_size.y)}); + ImGuiWrapper::draw(hull); + ImGuiWrapper::draw(rect); +} + void GLGizmoEmboss::draw_advanced() { const auto &ff = m_style_manager.get_font_file_with_cache(); @@ -2965,40 +2985,6 @@ bool GLGizmoEmboss::choose_svg_file() //return add_volume(name, its); } -DataBase priv::create_emboss_data_base(const std::string &text, StyleManager& style_manager) -{ - auto create_volume_name = [&]() { - bool contain_enter = text.find('\n') != std::string::npos; - std::string text_fixed; - if (contain_enter) { - // change enters to space - text_fixed = text; // copy - std::replace(text_fixed.begin(), text_fixed.end(), '\n', ' '); - } - return _u8L("Text") + " - " + ((contain_enter) ? text_fixed : text); - }; - - auto create_configuration = [&]() -> TextConfiguration { - if (!style_manager.is_activ_font()) { - std::string default_text_for_emboss = _u8L("Embossed text"); - EmbossStyle es = style_manager.get_style(); - TextConfiguration tc{es, default_text_for_emboss}; - // TODO: investigate how to initialize - return tc; - } - - EmbossStyle &es = style_manager.get_style(); - // actualize font path - during changes in gui it could be corrupted - // volume must store valid path - assert(style_manager.get_wx_font().has_value()); - assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0); - // style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font()); - return TextConfiguration{es, text}; - }; - - return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), create_configuration(), create_volume_name()}; -} - bool GLGizmoEmboss::load_configuration(ModelVolume *volume) { if (volume == nullptr) return false; @@ -3251,5 +3237,202 @@ std::string GLGizmoEmboss::get_file_name(const std::string &file_path) return file_path.substr(offset, count); } +///////////// +// priv namespace implementation +/////////////// + +DataBase priv::create_emboss_data_base(const std::string &text, StyleManager& style_manager) +{ + auto create_volume_name = [&]() { + bool contain_enter = text.find('\n') != std::string::npos; + std::string text_fixed; + if (contain_enter) { + // change enters to space + text_fixed = text; // copy + std::replace(text_fixed.begin(), text_fixed.end(), '\n', ' '); + } + return _u8L("Text") + " - " + ((contain_enter) ? text_fixed : text); + }; + + auto create_configuration = [&]() -> TextConfiguration { + if (!style_manager.is_activ_font()) { + std::string default_text_for_emboss = _u8L("Embossed text"); + EmbossStyle es = style_manager.get_style(); + TextConfiguration tc{es, default_text_for_emboss}; + // TODO: investigate how to initialize + return tc; + } + + EmbossStyle &es = style_manager.get_style(); + // actualize font path - during changes in gui it could be corrupted + // volume must store valid path + assert(style_manager.get_wx_font().has_value()); + assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0); + // style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font()); + return TextConfiguration{es, text}; + }; + + return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), create_configuration(), create_volume_name()}; +} + + +Transform3d priv::create_transformation_on_bed(const Vec2d &screen_coor, const Camera &camera, const std::vector &bed_shape, double z) +{ + // Create new object + // calculate X,Y offset position for lay on platter in place of + // mouse click + Vec2d bed_coor = CameraUtils::get_z0_position(camera, screen_coor); + + // check point is on build plate: + Points bed_shape_; + bed_shape_.reserve(bed_shape.size()); + for (const Vec2d &p : bed_shape) bed_shape_.emplace_back(p.cast()); + Slic3r::Polygon bed(bed_shape_); + if (!bed.contains(bed_coor.cast())) + // mouse pose is out of build plate so create object in center of plate + bed_coor = bed.centroid().cast(); + + Vec3d offset(bed_coor.x(), bed_coor.y(), z); + // offset -= m_result.center(); + Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z()); + return Transform3d(tt); +} + +void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor) +{ + // start creation of new object + Plater *plater = wxGetApp().plater(); + const Camera &camera = plater->get_camera(); + const Pointfs &bed_shape = plater->build_volume().bed_shape(); + + // can't create new object with distance from surface + FontProp &prop = emboss_data.text_configuration.style.prop; + if (prop.distance.has_value()) prop.distance.reset(); + + // can't create new object with using surface + if (prop.use_surface) { + priv::message_disable_cut_surface(); + prop.use_surface = false; + } + + // Transform3d volume_tr = priv::create_transformation_on_bed(mouse_pos, camera, bed_shape, prop.emboss / 2); + DataCreateObject data{std::move(emboss_data), coor, camera, bed_shape}; + auto job = std::make_unique(std::move(data)); + Worker &worker = plater->get_ui_job_worker(); + queue_job(worker, std::move(job)); +} + +void priv::start_create_volume_job(const ModelObject *object, + const Transform3d volume_trmat, + DataBase &emboss_data, + ModelVolumeType volume_type) +{ + bool &use_surface = emboss_data.text_configuration.style.prop.use_surface; + std::unique_ptr job; + if (use_surface) { + // Model to cut surface from. + SurfaceVolumeData::ModelSources sources = create_sources(object->volumes); + if (sources.empty()) { + priv::message_disable_cut_surface(); + use_surface = false; + } else { + bool is_outside = volume_type == ModelVolumeType::MODEL_PART; + // check that there is not unexpected volume type + assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER); + CreateSurfaceVolumeData surface_data{std::move(emboss_data), volume_trmat, is_outside, + std::move(sources), volume_type, object->id()}; + job = std::make_unique(std::move(surface_data)); + } + } + if (!use_surface) { + // create volume + DataCreateVolume data{std::move(emboss_data), volume_type, object->id(), volume_trmat}; + job = std::make_unique(std::move(data)); + } + + Plater *plater = wxGetApp().plater(); + Worker &worker = plater->get_ui_job_worker(); + queue_job(worker, std::move(job)); +} + +GLVolume * priv::get_hovered_gl_volume(const GLCanvas3D &canvas) { + int hovered_id_signed = canvas.get_first_hover_volume_idx(); + if (hovered_id_signed < 0) return nullptr; + + size_t hovered_id = static_cast(hovered_id_signed); + const GLVolumePtrs &volumes = canvas.get_volumes().volumes; + if (hovered_id >= volumes.size()) return nullptr; + + return volumes[hovered_id]; +} + +bool priv::start_create_volume_on_surface_job( + DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster) +{ + if (gl_volume == nullptr) return false; + Plater *plater = wxGetApp().plater(); + const ModelObjectPtrs &objects = plater->model().objects; + + int object_idx = gl_volume->object_idx(); + if (object_idx < 0 || object_idx >= objects.size()) return false; + ModelObject *obj = objects[object_idx]; + size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id; + auto cond = RaycastManager::AllowVolumes({vol_id}); + raycaster.actualize(obj, &cond); + + const Camera &camera = plater->get_camera(); + std::optional hit = raycaster.unproject(screen_coor, 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 + // also hit must exist. But there is options to add text by object list + if (!hit.has_value()) return false; + + Transform3d hit_object_trmat = raycaster.get_transformation(hit->tr_key); + Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix(); + + // Create result volume transformation + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + const FontProp &font_prop = emboss_data.text_configuration.style.prop; + apply_transformation(font_prop, surface_trmat); + Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat; + start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); + return true; +} + +void priv::find_closest_volume(const Selection &selection, + const Vec2d &screen_center, + const Camera &camera, + const ModelObjectPtrs &objects, + Vec2d *closest_center, + const GLVolume **closest_volume) +{ + assert(closest_center != nullptr); + assert(closest_volume != nullptr); + assert(*closest_volume == nullptr); + const Selection::IndicesList &indices = selection.get_volume_idxs(); + assert(!indices.empty()); // no selected volume + if (indices.empty()) return; + + double center_sq_distance = std::numeric_limits::max(); + for (unsigned int id : indices) { + const GLVolume *gl_volume = selection.get_volume(id); + ModelVolume *volume = priv::get_model_volume(gl_volume, objects); + if (!volume->is_model_part()) continue; + Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume); + Vec2d c = hull.centroid().cast(); + Vec2d d = c - screen_center; + bool is_bigger_x = std::fabs(d.x()) > std::fabs(d.y()); + if ((is_bigger_x && d.x() * d.x() > center_sq_distance) || + (!is_bigger_x && d.y() * d.y() > center_sq_distance)) continue; + + double distance = d.squaredNorm(); + if (center_sq_distance < distance) continue; + center_sq_distance = distance; + *closest_center = c; + *closest_volume = gl_volume; + } +} + // any existing icon filename to not influence GUI const std::string GLGizmoEmboss::M_ICON_FILENAME = "cut.svg"; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index f8a6a4d1f1..092041e96e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -44,13 +44,13 @@ public: /// /// Object part / Negative volume / Modifier /// Define position of new volume - void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos = Vec2d(-1,-1)); + void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos); /// - /// Move window for edit emboss text near to embossed object - /// NOTE: embossed object must be selected + /// Create new text without given position /// - void set_fine_position(); + /// Object part / Negative volume / Modifier + void create_volume(ModelVolumeType volume_type); protected: bool on_init() override; @@ -91,8 +91,6 @@ private: // localized default text void set_default_text(); - bool start_volume_creation(ModelVolumeType volume_type, const Vec2d &screen_coor); - void check_selection(); ModelVolume *get_selected_volume(); // create volume from text - main functionality @@ -125,6 +123,12 @@ private: void do_translate(const Vec3d& relative_move); void do_rotate(float relative_z_angle); + /// + /// Move window for edit emboss text near to embossed object + /// NOTE: embossed object must be selected + /// + void set_fine_position(); + /// /// Reversible input float with option to restor default value /// TODO: make more general, static and move to ImGuiWrapper diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index f49e8499bd..56d8c7e454 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -69,11 +69,11 @@ static void update_volume(TriangleMesh &&mesh, const DataUpdate &data); /// Add new volume to object /// /// triangles of new volume -/// Object where to add volume +/// Object where to add volume /// Type of new volume /// Transformation of volume inside of object /// Text configuration and New VolumeName -static void create_volume(TriangleMesh &&mesh, const size_t object_idx, +static void create_volume(TriangleMesh &&mesh, const ObjectID& object_id, const ModelVolumeType type, const Transform3d trmat, const DataBase &data); /// @@ -147,7 +147,7 @@ void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { if (m_result.its.empty()) return priv::create_message(_u8L("Can't create empty volume.")); - priv::create_volume(std::move(m_result), m_input.object_idx, m_input.volume_type, m_input.trmat, m_input); + priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, m_input); } @@ -320,7 +320,7 @@ void CreateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { // TODO: Find better way to Not center volume data when add !!! TriangleMesh mesh = m_result; // Part1: copy - priv::create_volume(std::move(m_result), m_input.object_idx, + priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.text_tr, m_input); // Part2: update volume data @@ -385,10 +385,8 @@ bool priv::check(const DataCreateVolume &input, bool is_main_thread) { bool res = check((DataBase) input, check_fontfile); assert(input.volume_type != ModelVolumeType::INVALID); res &= input.volume_type != ModelVolumeType::INVALID; - assert(input.object_idx >= 0); - res &= input.object_idx >= 0; - if (is_main_thread) - assert((size_t)input.object_idx < wxGetApp().model().objects.size()); + assert(input.object_id.id >= 0); + res &= input.object_id.id >= 0; return res; } bool priv::check(const DataCreateObject &input) { @@ -571,7 +569,7 @@ void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data) } void priv::create_volume( - TriangleMesh &&mesh, const size_t object_idx, + TriangleMesh &&mesh, const ObjectID& object_id, const ModelVolumeType type, const Transform3d trmat, const DataBase &data) { GUI_App &app = wxGetApp(); @@ -580,18 +578,26 @@ void priv::create_volume( GLCanvas3D *canvas = plater->canvas3D(); ModelObjectPtrs &objects = plater->model().objects; + ModelObject *obj = nullptr; + size_t object_idx = 0; + for (; object_idx < objects.size(); ++object_idx) { + ModelObject *o = objects[object_idx]; + if (o->id() == object_id) { + obj = o; + break; + } + } + // Parent object for text volume was propably removed. // Assumption: User know what he does, so text volume is no more needed. - if (objects.size() <= object_idx) - return priv::create_message(_u8L("Bad object index to create volume.")); + if (obj == nullptr) + return priv::create_message(_u8L("Bad object to create volume.")); if (mesh.its.empty()) return priv::create_message(_u8L("Can't create empty volume.")); plater->take_snapshot(_L("Add Emboss text Volume")); - ModelObject *obj = objects[object_idx]; - // NOTE: be carefull add volume also center mesh !!! // So first add simple shape(convex hull is also calculated) ModelVolume *volume = obj->add_volume(make_cube(1., 1., 1.), type); @@ -618,7 +624,7 @@ void priv::create_volume( // select only actual volume // when new volume is created change selection to this volume auto add_to_selection = [volume](const ModelVolume *vol) { return vol == volume; }; - wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(object_idx, add_to_selection); + wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(object_idx, add_to_selection); if (!sel.IsEmpty()) obj_list->select_item(sel.front()); // update printable state on canvas diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index cf64044bf0..c8ef7fee64 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -41,7 +41,7 @@ struct DataCreateVolume : public DataBase ModelVolumeType volume_type; // parent ModelObject index where to create volume - int object_idx; + ObjectID object_id; // new created volume transformation Transform3d trmat; @@ -178,7 +178,7 @@ struct CreateSurfaceVolumeData : public DataBase, public SurfaceVolumeData{ ModelVolumeType volume_type; // parent ModelObject index where to create volume - int object_idx; + ObjectID object_id; }; ///