NEW: Support attaching 3D text to a surface

Change-Id: I13f234cf5f189b594a6277d623dfa9dee489d19f
(cherry picked from commit 88ec90226aecb8203c105569a613d33c68a0602b)
This commit is contained in:
zhimin.zeng 2023-02-14 15:03:10 +08:00 committed by Lane.Wei
parent 85bcb8eae8
commit 1041b8b026
6 changed files with 1089 additions and 98 deletions

View File

@ -650,6 +650,16 @@ private:
friend class ModelVolume; friend class ModelVolume;
}; };
struct RaycastResult
{
Vec2d mouse_position = Vec2d::Zero();
int mesh_id = -1;
Vec3f hit = Vec3f::Zero();
Vec3f normal = Vec3f::Zero();
template<typename Archive> void serialize(Archive &ar) { ar(mouse_position, mesh_id, hit, normal); }
};
struct TextInfo struct TextInfo
{ {
std::string m_font_name; std::string m_font_name;
@ -658,9 +668,18 @@ struct TextInfo
bool m_bold = true; bool m_bold = true;
bool m_italic = false; bool m_italic = false;
float m_thickness = 2.f; float m_thickness = 2.f;
float m_embeded_depth = 0.f;
float m_rotate_angle = 0;
float m_text_gap = 0.f;
bool m_is_surface_text = false;
bool m_keep_horizontal = false;
std::string m_text; std::string m_text;
template<typename Archive> void serialize(Archive &ar) { ar(m_font_name, m_font_size, m_curr_font_idx, m_bold, m_italic, m_thickness, m_text); } RaycastResult m_rr;
template<typename Archive> void serialize(Archive &ar) {
ar(m_font_name, m_font_size, m_curr_font_idx, m_bold, m_italic, m_thickness, m_embeded_depth, m_rotate_angle, m_text_gap, m_is_surface_text, m_keep_horizontal, m_text, m_rr);
}
}; };
// An object STL, or a modifier volume, over which a different set of parameters shall be applied. // An object STL, or a modifier volume, over which a different set of parameters shall be applied.
@ -914,7 +933,8 @@ private:
ObjectBase(other), ObjectBase(other),
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets) supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets),
m_text_info(other.m_text_info)
{ {
assert(this->id().valid()); assert(this->id().valid());
assert(this->config.id().valid()); assert(this->config.id().valid());

View File

@ -2170,52 +2170,54 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
#endif /* _DEBUG */ #endif /* _DEBUG */
} }
void ObjectList::load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool center) int ObjectList::load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool is_temp)
{ {
wxDataViewItem item = GetSelection(); wxDataViewItem item = GetSelection();
// we can add volumes for Object or Instance // we can add volumes for Object or Instance
if (!item || !(m_objects_model->GetItemType(item) & (itObject | itInstance))) if (!item || !(m_objects_model->GetItemType(item) & (itObject | itInstance)))
return; return -1;
const int obj_idx = m_objects_model->GetObjectIdByItem(item); const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx < 0) return; if (obj_idx < 0)
return -1;
// Get object item, if Instance is selected // Get object item, if Instance is selected
if (m_objects_model->GetItemType(item) & itInstance) if (m_objects_model->GetItemType(item) & itInstance)
item = m_objects_model->GetItemById(obj_idx); item = m_objects_model->GetItemById(obj_idx);
take_snapshot("Load Mesh Part");
ModelObject* mo = (*m_objects)[obj_idx]; ModelObject* mo = (*m_objects)[obj_idx];
Geometry::Transformation instance_transformation = mo->instances[0]->get_transformation();
// apply the instance transform to all volumes and reset instance transform except the offset // apply the instance transform to all volumes and reset instance transform except the offset
apply_object_instance_transfrom_to_all_volumes(mo); apply_object_instance_transfrom_to_all_volumes(mo);
double old_top_position = mo->mesh().bounding_box().max(2) - mo->instances[0]->get_offset().z(); ModelVolume *mv = mo->add_volume(mesh);
ModelVolume* mv = mo->add_volume(mesh);
Vec3d offset = Vec3d(0, 0, old_top_position + mv->get_offset(Axis::Z));
mv->set_offset(offset);
mv->name = name.ToStdString(); mv->name = name.ToStdString();
if (!text_info.m_text.empty()) if (!text_info.m_text.empty())
mv->set_text_info(text_info); mv->set_text_info(text_info);
std::vector<ModelVolume*> volumes; if (!is_temp) {
volumes.push_back(mv); std::vector<ModelVolume *> volumes;
wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) { volumes.push_back(mv);
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); }); wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume *volume) {
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end();
});
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx); wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_object((size_t) obj_idx);
if (items.size() > 1) { if (items.size() > 1) {
m_selection_mode = smVolume; m_selection_mode = smVolume;
m_last_selected_item = wxDataViewItem(nullptr); m_last_selected_item = wxDataViewItem(nullptr);
}
select_items(items);
selection_changed();
} }
select_items(items);
selection_changed();
//BBS: notify partplate the modify //BBS: notify partplate the modify
notify_instance_updated(obj_idx); notify_instance_updated(obj_idx);
return mo->volumes.size() - 1;
} }
//BBS //BBS

View File

@ -284,7 +284,7 @@ public:
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true); void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
// BBS // BBS
void switch_to_object_process(); void switch_to_object_process();
void load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool center = true); int load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool is_temp);
void del_object(const int obj_idx, bool refresh_immediately = true); void del_object(const int obj_idx, bool refresh_immediately = true);
void del_subobject_item(wxDataViewItem& item); void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config(const wxDataViewItem& parent_item); void del_settings_from_config(const wxDataViewItem& parent_item);

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,8 @@
#include "GLGizmoBase.hpp" #include "GLGizmoBase.hpp"
#include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/3DScene.hpp"
#include "../GLTexture.hpp" #include "../GLTexture.hpp"
#include "../Camera.hpp"
#include "libslic3r/Model.hpp"
namespace Slic3r { namespace Slic3r {
@ -12,6 +14,7 @@ class ModelVolume;
namespace GUI { namespace GUI {
enum class SLAGizmoEventType : unsigned char;
class GLGizmoText : public GLGizmoBase class GLGizmoText : public GLGizmoBase
{ {
private: private:
@ -23,10 +26,21 @@ private:
bool m_bold = true; bool m_bold = true;
bool m_italic = false; bool m_italic = false;
float m_thickness = 2.f; float m_thickness = 2.f;
float m_embeded_depth = 0.f;
float m_rotate_angle = 0;
float m_text_gap = 0.f;
bool m_is_surface_text = false;
bool m_keep_horizontal = false;
mutable RaycastResult m_rr;
float m_combo_height = 0.0f; float m_combo_height = 0.0f;
float m_combo_width = 0.0f; float m_combo_width = 0.0f;
float m_scale; float m_scale;
Vec2d m_mouse_position = Vec2d::Zero();
Vec2d m_origin_mouse_position = Vec2d::Zero();
bool m_shift_down = false;
class TextureInfo { class TextureInfo {
public: public:
GLTexture* texture { nullptr }; GLTexture* texture { nullptr };
@ -42,8 +56,24 @@ private:
std::vector<std::string> m_font_names; std::vector<std::string> m_font_names;
bool m_is_modify = false; bool m_is_modify = false;
int m_object_idx; bool m_need_update_text = false;
int m_volume_idx;
int m_object_idx = -1;
int m_volume_idx = -1;
int m_preview_text_volume_id = -1;
Vec3d m_mouse_position_world = Vec3d::Zero();
Vec3d m_mouse_normal_world = Vec3d::Zero();
Vec3d m_cut_plane_dir = Vec3d::UnitZ();
std::vector<Vec3d> m_position_points;
std::vector<Vec3d> m_normal_points;
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc;
public: public:
GLGizmoText(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); GLGizmoText(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
@ -51,6 +81,11 @@ public:
void update_font_texture(); void update_font_texture();
bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down);
bool is_mesh_point_clipped(const Vec3d &point, const Transform3d &trafo) const;
BoundingBoxf3 bounding_box() const;
protected: protected:
virtual bool on_init() override; virtual bool on_init() override;
virtual std::string on_get_name() const override; virtual std::string on_get_name() const override;
@ -65,9 +100,20 @@ protected:
virtual CommonGizmosDataID on_get_requirements() const override; virtual CommonGizmosDataID on_get_requirements() const override;
virtual void on_render_input_window(float x, float y, float bottom_limit); virtual void on_render_input_window(float x, float y, float bottom_limit);
void show_tooltip_information(float x, float y);
private: private:
ModelVolume *get_selected_single_volume(int& out_object_idx, int& out_volume_idx) const; ModelVolume *get_selected_single_volume(int& out_object_idx, int& out_volume_idx) const;
void reset_text_info(); void reset_text_info();
bool update_text_positions(const std::vector<std::string>& texts);
TriangleMesh get_text_mesh(const char* text_str, const Vec3d &position, const Vec3d &normal, const Vec3d &text_up_dir);
bool update_raycast_cache(const Vec2d &mouse_position, const Camera &camera, const std::vector<Transform3d> &trafo_matrices);
void generate_text_volume(bool is_temp = true);
void delete_temp_preview_text_volume();
TextInfo get_text_info();
void load_from_text_info(const TextInfo &text_info);
}; };
} // namespace GUI } // namespace GUI

View File

@ -625,6 +625,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
return dynamic_cast<GLGizmoSeam*>(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoSeam*>(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == MmuSegmentation) else if (m_current == MmuSegmentation)
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Text)
return dynamic_cast<GLGizmoText*>(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else else
return false; return false;
} }
@ -757,7 +759,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
// mouse anywhere // mouse anywhere
if (evt.Moving()) { if (evt.Moving()) {
m_tooltip = update_hover_state(mouse_pos); m_tooltip = update_hover_state(mouse_pos);
if (m_current == MmuSegmentation || m_current == FdmSupports) if (m_current == MmuSegmentation || m_current == FdmSupports || m_current == Text)
// BBS // BBS
gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
} else if (evt.LeftUp()) { } else if (evt.LeftUp()) {
@ -870,7 +872,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
m_tooltip.clear(); m_tooltip.clear();
if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) {
if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Text)
&& gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown()))
// the gizmo got the event and took some action, there is no need to do anything more // the gizmo got the event and took some action, there is no need to do anything more
processed = true; processed = true;