diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index b78601af8..11b8d919d 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -458,6 +458,19 @@ void MenuFactory::append_menu_item_delete(wxMenu* menu) #endif } +void MenuFactory::append_menu_item_edit_text(wxMenu *menu) +{ +#ifdef __WINDOWS__ + append_menu_item( + menu, wxID_ANY, _L("Edit Text"), "", [](wxCommandEvent &) { plater()->edit_text(); }, "", nullptr, + []() { return plater()->can_edit_text(); }, m_parent); +#else + append_menu_item( + menu, wxID_ANY, _L("Edit Text"), "", [](wxCommandEvent &) { plater()->edit_text(); }, "", nullptr, + []() { return plater()->can_edit_text(); }, m_parent); +#endif +} + wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType type) { auto sub_menu = new wxMenu; @@ -1072,6 +1085,7 @@ void MenuFactory::create_bbl_part_menu() wxMenu* menu = &m_part_menu; append_menu_item_delete(menu); + append_menu_item_edit_text(menu); append_menu_item_fix_through_netfabb(menu); append_menu_item_simplify(menu); append_menu_item_center(menu); diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 1a2f90aba..a3c01f961 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -128,6 +128,7 @@ private: void append_menu_item_change_extruder(wxMenu* menu); void append_menu_item_set_visible(wxMenu* menu); void append_menu_item_delete(wxMenu* menu); + void append_menu_item_edit_text(wxMenu *menu); void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu); void append_menu_items_convert_unit(wxMenu* menu); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk" void append_menu_items_flush_options(wxMenu* menu); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index 0a0f3ff76..dab4b7eb8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -147,6 +147,8 @@ bool GLGizmoText::on_init() m_desc["rotate_text_caption"] = _L("Shift + Mouse movement"); m_desc["rotate_text"] = _L("Rotate preview text"); + + m_grabbers.push_back(Grabber()); return true; } @@ -215,6 +217,10 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit return false; const ModelObject * mo = m_c->selection_info()->model_object(); + if (m_is_modify) { + const Selection &selection = m_parent.get_selection(); + mo = selection.get_model()->objects[m_object_idx]; + } if (mo == nullptr) return false; @@ -232,6 +238,9 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit if (action == SLAGizmoEventType::Moving) { if (shift_down) { float angle = m_rotate_angle + 0.5 * (m_mouse_position - mouse_position).y(); + if (angle == 0) + return false; + while (angle < 0) angle += 360; @@ -240,6 +249,7 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit m_rotate_angle = angle; m_shift_down = true; + m_need_update_text = true; } else { m_shift_down = false; m_origin_mouse_position = mouse_position; @@ -247,6 +257,14 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit m_mouse_position = mouse_position; } else if (action == SLAGizmoEventType::LeftDown) { + if (!selection.is_empty() && get_hover_id() != -1) { + start_dragging(); + return true; + } + + if (m_is_modify) + return false; + Vec3f normal = Vec3f::Zero(); Vec3f hit = Vec3f::Zero(); size_t facet = 0; @@ -260,7 +278,9 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit if (mesh_id == m_preview_text_volume_id) continue; - if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + + if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), &facet)) { // In case this hit is clipped, skip it. if (is_mesh_point_clipped(hit.cast(), trafo_matrices[mesh_id])) @@ -352,6 +372,9 @@ bool GLGizmoText::on_is_activable() const void GLGizmoText::on_render() { + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + std::string text = std::string(m_text); if (text.empty()) { delete_temp_preview_text_volume(); @@ -378,7 +401,7 @@ void GLGizmoText::on_render() if (!plater) return; - if (!m_is_modify) { + if (!m_is_modify || m_shift_down) { const Camera &camera = wxGetApp().plater()->get_camera(); // Precalculate transformations of individual meshes. std::vector trafo_matrices; @@ -386,7 +409,18 @@ void GLGizmoText::on_render() if (mv->is_model_part()) trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); } // Raycast and return if there's no hit. - bool position_changed = update_raycast_cache(m_shift_down ? m_origin_mouse_position : m_parent.get_local_mouse_position(), camera, trafo_matrices); + Vec2d mouse_pos; + if (m_shift_down) { + if (m_is_modify) + mouse_pos = m_rr.mouse_position; + else + mouse_pos = m_origin_mouse_position; + } + else { + mouse_pos = m_parent.get_local_mouse_position(); + } + + bool position_changed = update_raycast_cache(mouse_pos, camera, trafo_matrices); if (m_rr.mesh_id == -1) { delete_temp_preview_text_volume(); @@ -396,6 +430,25 @@ void GLGizmoText::on_render() if (!position_changed && !m_need_update_text && !m_shift_down) return; } + + if (m_is_modify && m_grabbers.size() == 1) { + std::vector trafo_matrices; + for (const ModelVolume *mv : mo->volumes) { + if (mv->is_model_part()) { + trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); + } + } + + m_mouse_position_world = trafo_matrices[m_rr.mesh_id] * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2)); + + float mean_size = (float) (GLGizmoBase::Grabber::FixedGrabberSize); + + m_grabbers[0].center = m_mouse_position_world; + m_grabbers[0].enabled = true; + std::array color = picking_color_component(0); + m_grabbers[0].color = color; + m_grabbers[0].render_for_picking(mean_size); + } delete_temp_preview_text_volume(); @@ -408,7 +461,98 @@ void GLGizmoText::on_render() void GLGizmoText::on_render_for_picking() { - // TODO: + glsafe(::glDisable(GL_DEPTH_TEST)); + + int obejct_idx, volume_idx; + ModelVolume *model_volume = get_selected_single_volume(obejct_idx, volume_idx); + if (model_volume && !model_volume->get_text_info().m_text.empty()) { + if (m_grabbers.size() == 1) { + ModelObject *mo = m_c->selection_info()->model_object(); + if (m_is_modify) { + const Selection &selection = m_parent.get_selection(); + mo = selection.get_model()->objects[m_object_idx]; + } + if (mo == nullptr) return; + + const Selection & selection = m_parent.get_selection(); + const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; + + // Precalculate transformations of individual meshes. + std::vector trafo_matrices; + for (const ModelVolume *mv : mo->volumes) { + if (mv->is_model_part()) { + trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); + } + } + + m_mouse_position_world = trafo_matrices[m_rr.mesh_id] * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2)); + + float mean_size = (float) (GLGizmoBase::Grabber::FixedGrabberSize); + m_grabbers[0].center = m_mouse_position_world; + m_grabbers[0].enabled = true; + std::array color = picking_color_component(0); + m_grabbers[0].color = color; + m_grabbers[0].render_for_picking(mean_size); + } + } +} + +void GLGizmoText::on_update(const UpdateData &data) +{ + Vec2d mouse_pos = Vec2d(data.mouse_pos.x(), data.mouse_pos.y()); + const ModelObject *mo = m_c->selection_info()->model_object(); + if (m_is_modify) { + const Selection &selection = m_parent.get_selection(); + mo = selection.get_model()->objects[m_object_idx]; + } + if (mo == nullptr) return; + + const Selection & selection = m_parent.get_selection(); + const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; + const Camera & camera = wxGetApp().plater()->get_camera(); + + std::vector trafo_matrices; + for (const ModelVolume *mv : mo->volumes) { + if (mv->is_model_part()) { trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); } + } + + Vec3f normal = Vec3f::Zero(); + Vec3f hit = Vec3f::Zero(); + size_t facet = 0; + Vec3f closest_hit = Vec3f::Zero(); + Vec3f closest_normal = Vec3f::Zero(); + double closest_hit_squared_distance = std::numeric_limits::max(); + int closest_hit_mesh_id = -1; + + // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh + for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { + if (mesh_id == m_volume_idx) + continue; + + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + + if (mesh_raycaster.unproject_on_mesh(mouse_pos, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), + &facet)) { + // In case this hit is clipped, skip it. + if (is_mesh_point_clipped(hit.cast(), trafo_matrices[mesh_id])) continue; + + // Is this hit the closest to the camera so far? + double hit_squared_distance = (camera.get_position() - trafo_matrices[mesh_id] * hit.cast()).squaredNorm(); + if (hit_squared_distance < closest_hit_squared_distance) { + closest_hit_squared_distance = hit_squared_distance; + closest_hit_mesh_id = mesh_id; + closest_hit = hit; + closest_normal = normal; + } + } + } + + if (closest_hit == Vec3f::Zero() && closest_normal == Vec3f::Zero()) return; + + if (closest_hit_mesh_id != -1) { + m_rr = {mouse_pos, closest_hit_mesh_id, closest_hit, closest_normal}; + m_need_update_text = true; + } } void GLGizmoText::push_button_style(bool pressed) { @@ -533,11 +677,13 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) float depth_cap = m_imgui->calc_text_size(_L("Embeded depth")).x; float caption_size = std::max(std::max(font_cap, size_cap), std::max(depth_cap, input_cap)) + space_size + ImGui::GetStyle().WindowPadding.x; - float input_text_size = m_imgui->scaled(12.0f); + float input_text_size = m_imgui->scaled(10.0f); float button_size = ImGui::GetFrameHeight(); ImVec2 selectable_size(std::max((input_text_size + ImGui::GetFrameHeight() * 2), m_combo_width + SELECTABLE_INNER_OFFSET * currt_scale), m_combo_height); - float list_width = selectable_size.x + ImGui::GetStyle().ScrollbarSize + 2 * currt_scale; + //float list_width = selectable_size.x + ImGui::GetStyle().ScrollbarSize + 2 * currt_scale; + + float list_width = m_imgui->scaled(10.0f); float input_size = list_width - button_size * 2 - ImGui::GetStyle().ItemSpacing.x * 4; @@ -691,13 +837,13 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) float f_scale = m_parent.get_gizmos_manager().get_layout_scale(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); - ImGui::SameLine(); - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Status:")); - float status_cap = m_imgui->calc_text_size(_L("Status:")).x + space_size + ImGui::GetStyle().WindowPadding.x; - ImGui::SameLine(); - m_imgui->text(m_is_modify ? _L("Modify") : _L("Add")); - ImGui::PopStyleVar(2); + //ImGui::SameLine(); + //ImGui::AlignTextToFramePadding(); + //m_imgui->text(_L("Status:")); + //float status_cap = m_imgui->calc_text_size(_L("Status:")).x + space_size + ImGui::GetStyle().WindowPadding.x; + //ImGui::SameLine(); + //m_imgui->text(m_is_modify ? _L("Modify") : _L("Add")); + //ImGui::PopStyleVar(2); #if 0 ImGuiIO& io = ImGui::GetIO(); @@ -770,7 +916,7 @@ void GLGizmoText::reset_text_info() m_embeded_depth = 0.f; m_rotate_angle = 0; m_text_gap = 0.f; - m_is_surface_text = false; + m_is_surface_text = true; m_keep_horizontal = false; m_is_modify = false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp index 9fee02ce0..53b792364 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp @@ -92,6 +92,7 @@ protected: virtual bool on_is_activable() const override; virtual void on_render() override; virtual void on_render_for_picking() override; + virtual void on_update(const UpdateData &data) override; void push_combo_style(const float scale); void pop_combo_style(); void push_button_style(bool pressed); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5c638d085..a46d88434 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2081,6 +2081,7 @@ struct Plater::priv bool can_delete() const; bool can_delete_all() const; + bool can_edit_text() const; bool can_add_plate() const; bool can_delete_plate() const; bool can_increase_instances() const; @@ -6975,6 +6976,24 @@ bool Plater::priv::can_delete_all() const return !model.objects.empty(); } +bool Plater::priv::can_edit_text() const +{ + const Selection &selection = view3D->get_canvas3d()->get_selection(); + if (selection.is_single_full_instance()) + return true; + + if (selection.is_single_volume()) { + const GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin()); + int out_object_idx = gl_volume->object_idx(); + ModelObject * model_object = selection.get_model()->objects[out_object_idx]; + int out_volume_idx = gl_volume->volume_idx(); + ModelVolume * model_volume = model_object->volumes[out_volume_idx]; + if (model_volume) + return !model_volume->get_text_info().m_text.empty(); + } + return false; +} + bool Plater::priv::can_add_plate() const { return q->get_partplate_list().get_plate_count() < PartPlateList::MAX_PLATES_COUNT; @@ -11399,6 +11418,14 @@ void Plater::show_status_message(std::string s) BOOST_LOG_TRIVIAL(trace) << "show_status_message:" << s; } +void Plater::edit_text() +{ + auto &manager = get_view3D_canvas3D()->get_gizmos_manager(); + manager.open_gizmo(GLGizmosManager::Text); + update(); +} + +bool Plater::can_edit_text() const { return p->can_edit_text(); } bool Plater::can_delete() const { return p->can_delete(); } bool Plater::can_delete_all() const { return p->can_delete_all(); } bool Plater::can_add_model() const { return !is_background_process_slicing(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 97a90653d..56d97d83a 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -447,6 +447,10 @@ public: //BBS: void fill_color(int extruder_id); + //BBS: + void edit_text(); + bool can_edit_text() const; + bool can_delete() const; bool can_delete_all() const; bool can_add_model() const;