diff --git a/resources/images/toolbar_text.svg b/resources/images/toolbar_text.svg
new file mode 100644
index 000000000..392ae3685
--- /dev/null
+++ b/resources/images/toolbar_text.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index da327ec97..2ebdb72e0 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -315,7 +315,6 @@ set(lisbslic3r_sources
SLA/Clustering.hpp
SLA/Clustering.cpp
SLA/ReprojectPointsOnMesh.hpp
-
Arachne/BeadingStrategy/BeadingStrategy.hpp
Arachne/BeadingStrategy/BeadingStrategy.cpp
Arachne/BeadingStrategy/BeadingStrategyFactory.hpp
@@ -356,6 +355,8 @@ set(lisbslic3r_sources
Arachne/SkeletalTrapezoidationJoint.hpp
Arachne/WallToolPaths.hpp
Arachne/WallToolPaths.cpp
+ Shape/TextShape.hpp
+ Shape/TextShape.cpp
)
if (APPLE)
diff --git a/src/libslic3r/Shape/TextShape.cpp b/src/libslic3r/Shape/TextShape.cpp
new file mode 100644
index 000000000..9185c1b01
--- /dev/null
+++ b/src/libslic3r/Shape/TextShape.cpp
@@ -0,0 +1,203 @@
+#include "../libslic3r.h"
+#include "../Model.hpp"
+#include "../TriangleMesh.hpp"
+
+#include "TextShape.hpp"
+
+#include
+#include
+
+#include "Standard_TypeDef.hxx"
+#include "STEPCAFControl_Reader.hxx"
+#include "BRepMesh_IncrementalMesh.hxx"
+#include "Interface_Static.hxx"
+#include "XCAFDoc_DocumentTool.hxx"
+#include "XCAFDoc_ShapeTool.hxx"
+#include "XCAFApp_Application.hxx"
+#include "TopoDS_Solid.hxx"
+#include "TopoDS_Compound.hxx"
+#include "TopoDS_Builder.hxx"
+#include "TopoDS.hxx"
+#include "TDataStd_Name.hxx"
+#include "BRepBuilderAPI_Transform.hxx"
+#include "TopExp_Explorer.hxx"
+#include "TopExp_Explorer.hxx"
+#include "BRep_Tool.hxx"
+#include "Font_BRepFont.hxx"
+#include "Font_BRepTextBuilder.hxx"
+#include "BRepPrimAPI_MakePrism.hxx"
+#include "Font_FontMgr.hxx"
+
+namespace Slic3r {
+
+std::vector init_occt_fonts()
+{
+ std::vector stdFontNames;
+
+ Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
+ aFontMgr->InitFontDataBase();
+
+ TColStd_SequenceOfHAsciiString availFontNames;
+ aFontMgr->GetAvailableFontsNames(availFontNames);
+ stdFontNames.reserve(availFontNames.Size());
+
+ for (auto afn : availFontNames)
+ stdFontNames.push_back(afn->ToCString());
+
+ return stdFontNames;
+}
+
+static bool TextToBRep(const char* text, const char* font, const float theTextHeight, Font_FontAspect& theFontAspect, TopoDS_Shape& theShape)
+{
+ Standard_Integer anArgIt = 1;
+ Standard_CString aName = "text_shape";
+ Standard_CString aText = text;
+
+ Font_BRepFont aFont;
+ //TCollection_AsciiString aFontName("Courier");
+ TCollection_AsciiString aFontName(font);
+ Standard_Real aTextHeight = theTextHeight;
+ Font_FontAspect aFontAspect = theFontAspect;
+ Standard_Boolean anIsCompositeCurve = Standard_False;
+ gp_Ax3 aPenAx3(gp::XOY());
+ gp_Dir aNormal(0.0, 0.0, 1.0);
+ gp_Dir aDirection(1.0, 0.0, 0.0);
+ gp_Pnt aPenLoc;
+
+ Graphic3d_HorizontalTextAlignment aHJustification = Graphic3d_HTA_LEFT;
+ Graphic3d_VerticalTextAlignment aVJustification = Graphic3d_VTA_BOTTOM;
+ Font_StrictLevel aStrictLevel = Font_StrictLevel_Any;
+
+ aFont.SetCompositeCurveMode(anIsCompositeCurve);
+ if (!aFont.FindAndInit(aFontName.ToCString(), aFontAspect, aTextHeight, aStrictLevel))
+ return false;
+
+ aPenAx3 = gp_Ax3(aPenLoc, aNormal, aDirection);
+
+ Font_BRepTextBuilder aBuilder;
+ theShape = aBuilder.Perform(aFont, aText, aPenAx3, aHJustification, aVJustification);
+ return true;
+}
+
+static bool Prism(const TopoDS_Shape& theBase, const float thickness, TopoDS_Shape& theSolid)
+{
+ if (theBase.IsNull()) return false;
+
+ gp_Vec V(0.f, 0.f, thickness);
+ BRepPrimAPI_MakePrism* Prism = new BRepPrimAPI_MakePrism(theBase, V, Standard_False);
+
+ theSolid = Prism->Shape();
+ return true;
+}
+
+static void MakeMesh(TopoDS_Shape& theSolid, TriangleMesh& theMesh)
+{
+ const double STEP_TRANS_CHORD_ERROR = 0.005;
+ const double STEP_TRANS_ANGLE_RES = 1;
+
+ BRepMesh_IncrementalMesh mesh(theSolid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
+ int aNbNodes = 0;
+ int aNbTriangles = 0;
+ for (TopExp_Explorer anExpSF(theSolid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
+ TopLoc_Location aLoc;
+ Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
+ if (!aTriangulation.IsNull()) {
+ aNbNodes += aTriangulation->NbNodes();
+ aNbTriangles += aTriangulation->NbTriangles();
+ }
+ }
+
+ stl_file stl;
+ stl.stats.type = inmemory;
+ stl.stats.number_of_facets = (uint32_t)aNbTriangles;
+ stl.stats.original_num_facets = stl.stats.number_of_facets;
+ stl_allocate(&stl);
+
+ std::vector points;
+ points.reserve(aNbNodes);
+ //BBS: count faces missing triangulation
+ Standard_Integer aNbFacesNoTri = 0;
+ //BBS: fill temporary triangulation
+ Standard_Integer aNodeOffset = 0;
+ Standard_Integer aTriangleOffet = 0;
+ for (TopExp_Explorer anExpSF(theSolid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
+ const TopoDS_Shape& aFace = anExpSF.Current();
+ TopLoc_Location aLoc;
+ Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
+ if (aTriangulation.IsNull()) {
+ ++aNbFacesNoTri;
+ continue;
+ }
+ //BBS: copy nodes
+ gp_Trsf aTrsf = aLoc.Transformation();
+ for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
+ gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
+ aPnt.Transform(aTrsf);
+ points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
+ }
+ //BBS: copy triangles
+ const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
+ for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
+ Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
+
+ Standard_Integer anId[3];
+ aTri.Get(anId[0], anId[1], anId[2]);
+ if (anOrientation == TopAbs_REVERSED) {
+ //BBS: swap 1, 2.
+ Standard_Integer aTmpIdx = anId[1];
+ anId[1] = anId[2];
+ anId[2] = aTmpIdx;
+ }
+ //BBS: Update nodes according to the offset.
+ anId[0] += aNodeOffset;
+ anId[1] += aNodeOffset;
+ anId[2] += aNodeOffset;
+ //BBS: save triangles facets
+ stl_facet facet;
+ facet.vertex[0] = points[anId[0] - 1].cast();
+ facet.vertex[1] = points[anId[1] - 1].cast();
+ facet.vertex[2] = points[anId[2] - 1].cast();
+ facet.extra[0] = 0;
+ facet.extra[1] = 0;
+ stl_normal normal;
+ stl_calculate_normal(normal, &facet);
+ stl_normalize_vector(normal);
+ facet.normal = normal;
+ stl.facet_start[aTriangleOffet + aTriIter - 1] = facet;
+ }
+
+ aNodeOffset += aTriangulation->NbNodes();
+ aTriangleOffet += aTriangulation->NbTriangles();
+ }
+
+ theMesh.from_stl(stl);
+}
+
+void load_text_shape(const char*text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh)
+{
+ Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
+ if (aFontMgr->GetAvailableFonts().IsEmpty())
+ aFontMgr->InitFontDataBase();
+
+ TopoDS_Shape aTextBase;
+ Font_FontAspect aFontAspect = Font_FontAspect_UNDEFINED;
+ if (is_bold && is_italic)
+ aFontAspect = Font_FontAspect_BoldItalic;
+ else if (is_bold)
+ aFontAspect = Font_FontAspect_Bold;
+ else if (is_italic)
+ aFontAspect = Font_FontAspect_Italic;
+ else
+ aFontAspect = Font_FontAspect_Regular;
+
+ if (!TextToBRep(text, font, text_height, aFontAspect, aTextBase))
+ return;
+
+ TopoDS_Shape aTextShape;
+ if (!Prism(aTextBase, thickness, aTextShape))
+ return;
+
+ MakeMesh(aTextShape, text_mesh);
+}
+
+}; // namespace Slic3r
diff --git a/src/libslic3r/Shape/TextShape.hpp b/src/libslic3r/Shape/TextShape.hpp
new file mode 100644
index 000000000..0da84bb95
--- /dev/null
+++ b/src/libslic3r/Shape/TextShape.hpp
@@ -0,0 +1,12 @@
+#ifndef slic3r_Text_Shape_hpp_
+#define slic3r_Text_Shape_hpp_
+
+namespace Slic3r {
+class TriangleMesh;
+
+extern std::vector init_occt_fonts();
+extern void load_text_shape(const char* text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh);
+
+}; // namespace Slic3r
+
+#endif // slic3r_Text_Shape_hpp_
\ No newline at end of file
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index ca8f445e3..e310808ff 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -129,6 +129,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoFaceDetector.hpp
GUI/Gizmos/GLGizmoSeam.cpp
GUI/Gizmos/GLGizmoSeam.hpp
+ GUI/Gizmos/GLGizmoText.cpp
+ GUI/Gizmos/GLGizmoText.hpp
GUI/GLSelectionRectangle.cpp
GUI/GLSelectionRectangle.hpp
GUI/Gizmos/GizmoObjectManipulation.cpp
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index b93c0de6a..8c9a42b80 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -2069,6 +2069,45 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
#endif /* _DEBUG */
}
+void ObjectList::load_mesh_part(const TriangleMesh& mesh, const wxString& name, bool center)
+{
+ wxDataViewItem item = GetSelection();
+ // we can add volumes for Object or Instance
+ if (!item || !(m_objects_model->GetItemType(item) & (itObject | itInstance)))
+ return;
+ const int obj_idx = m_objects_model->GetObjectIdByItem(item);
+
+ if (obj_idx < 0) return;
+
+ // Get object item, if Instance is selected
+ if (m_objects_model->GetItemType(item) & itInstance)
+ item = m_objects_model->GetItemById(obj_idx);
+
+ take_snapshot("Load Mesh Part");
+
+ ModelObject* mo = (*m_objects)[obj_idx];
+ ModelVolume* mv = mo->add_volume(mesh);
+ mv->name = name.ToStdString();
+
+ std::vector volumes;
+ volumes.push_back(mv);
+ 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()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
+
+ if (items.size() > 1) {
+ m_selection_mode = smVolume;
+ m_last_selected_item = wxDataViewItem(nullptr);
+ }
+ select_items(items);
+
+ selection_changed();
+
+ //BBS: notify partplate the modify
+ notify_instance_updated(obj_idx);
+}
+
//BBS
void ObjectList::del_object(const int obj_idx, bool refresh_immediately)
{
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index dac269846..f57c8fc29 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -281,6 +281,8 @@ public:
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
void load_shape_object(const std::string &type_name);
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
+ // BBS
+ void load_mesh_part(const TriangleMesh& mesh, const wxString& name, bool center = true);
void del_object(const int obj_idx, bool refresh_immediately = true);
void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config(const wxDataViewItem& parent_item);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp
new file mode 100644
index 000000000..8ce8931cd
--- /dev/null
+++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp
@@ -0,0 +1,137 @@
+// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
+#include "GLGizmoText.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
+#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
+#include "slic3r/GUI/GUI_App.hpp"
+#include "slic3r/GUI/ImGuiWrapper.hpp"
+#include "slic3r/GUI/GUI_ObjectList.hpp"
+
+#include "libslic3r/Geometry/ConvexHull.hpp"
+#include "libslic3r/Model.hpp"
+
+#include "libslic3r/Shape/TextShape.hpp"
+
+#include
+
+#include
+
+namespace Slic3r {
+namespace GUI {
+
+static double g_normal_precise = 0.0015;
+
+GLGizmoText::GLGizmoText(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
+ : GLGizmoBase(parent, icon_filename, sprite_id)
+{
+}
+
+bool GLGizmoText::on_init()
+{
+ m_avail_font_names = init_occt_fonts();
+ m_shortcut_key = WXK_CONTROL_T;
+ return true;
+}
+
+void GLGizmoText::on_set_state()
+{
+}
+
+CommonGizmosDataID GLGizmoText::on_get_requirements() const
+{
+ return CommonGizmosDataID::SelectionInfo;
+}
+
+std::string GLGizmoText::on_get_name() const
+{
+ return _u8L("Text shape");
+}
+
+bool GLGizmoText::on_is_activable() const
+{
+ // This is assumed in GLCanvas3D::do_rotate, do not change this
+ // without updating that function too.
+ return m_parent.get_selection().is_single_full_instance();
+}
+
+void GLGizmoText::on_render()
+{
+ // TODO:
+}
+
+void GLGizmoText::on_render_for_picking()
+{
+ // TODO:
+}
+
+// BBS
+void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
+{
+ static float last_y = 0.0f;
+ static float last_h = 0.0f;
+
+ float space_size = m_imgui->get_style_scaling() * 8;
+ float font_cap = m_imgui->calc_text_size("Font ").x;
+ float size_cap = m_imgui->calc_text_size("Size ").x;
+ float thickness_cap = m_imgui->calc_text_size("Thickness ").x;
+ float caption_size = std::max(std::max(font_cap, size_cap), thickness_cap) + 2 * space_size;
+
+ m_imgui->begin(_L("Text"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
+
+ // adjust window position to avoid overlap the view toolbar
+ const float win_h = ImGui::GetWindowHeight();
+ y = std::min(y, bottom_limit - win_h);
+ ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
+ if (last_h != win_h || last_y != y) {
+ // ask canvas for another frame to render the window in the correct position
+ m_imgui->set_requires_extra_frame();
+ if (last_h != win_h)
+ last_h = win_h;
+ if (last_y != y)
+ last_y = y;
+ }
+
+ ImGui::AlignTextToFramePadding();
+
+ const char** cstr_font_names = (const char**)calloc(m_avail_font_names.size(), sizeof(const char*));
+ for (int i = 0; i < m_avail_font_names.size(); i++)
+ cstr_font_names[i] = m_avail_font_names[i].c_str();
+
+ ImGui::InputText("", m_text, sizeof(m_text));
+
+ ImGui::PushItemWidth(caption_size);
+ ImGui::Text("Font ");
+ ImGui::SameLine();
+ ImGui::PushItemWidth(150);
+ ImGui::Combo("##Font", &m_curr_font_idx, cstr_font_names, m_avail_font_names.size());
+
+ ImGui::PushItemWidth(caption_size);
+ ImGui::Text("Size ");
+ ImGui::SameLine();
+ ImGui::PushItemWidth(150);
+ ImGui::InputFloat("###font_size", &m_font_size);
+
+ ImGui::PushItemWidth(caption_size);
+ ImGui::Text("Thickness ");
+ ImGui::SameLine();
+ ImGui::PushItemWidth(150);
+ ImGui::InputFloat("###text_thickness", &m_thickness);
+
+ ImGui::Checkbox("Bold", &m_bold);
+ ImGui::SameLine();
+ ImGui::Checkbox("Italic", &m_italic);
+
+ ImGui::Separator();
+
+ bool add_clicked = m_imgui->button(_L("Add"));
+ if (add_clicked) {
+ TriangleMesh mesh;
+ load_text_shape(m_text, m_font_name.c_str(), m_font_size, m_thickness, m_bold, m_italic, mesh);
+ ObjectList* obj_list = wxGetApp().obj_list();
+ obj_list->load_mesh_part(mesh, "text_shape");
+ }
+
+ m_imgui->end();
+}
+
+} // namespace GUI
+} // namespace Slic3r
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp
new file mode 100644
index 000000000..dd73c74a2
--- /dev/null
+++ b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp
@@ -0,0 +1,43 @@
+#ifndef slic3r_GLGizmoText_hpp_
+#define slic3r_GLGizmoText_hpp_
+
+#include "GLGizmoBase.hpp"
+#include "slic3r/GUI/3DScene.hpp"
+
+
+namespace Slic3r {
+
+enum class ModelVolumeType : int;
+
+namespace GUI {
+
+class GLGizmoText : public GLGizmoBase
+{
+private:
+ std::vector m_avail_font_names;
+ char m_text[256] = { 0 };
+ std::string m_font_name;
+ float m_font_size = 16.f;
+ int m_curr_font_idx = 0;
+ bool m_bold = true;
+ bool m_italic = false;
+ float m_thickness = 2.f;
+
+public:
+ GLGizmoText(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
+
+protected:
+ virtual bool on_init() override;
+ virtual std::string on_get_name() const override;
+ virtual bool on_is_activable() const override;
+ virtual void on_render() override;
+ virtual void on_render_for_picking() override;
+ virtual void on_set_state() override;
+ virtual CommonGizmosDataID on_get_requirements() const override;
+ virtual void on_render_input_window(float x, float y, float bottom_limit);
+};
+
+} // namespace GUI
+} // namespace Slic3r
+
+#endif // slic3r_GLGizmoText_hpp_
\ No newline at end of file
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index 21a8be5bc..36e97aedd 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -22,6 +22,7 @@
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
+#include "slic3r/GUI/Gizmos/GLGizmoText.hpp"
#include "libslic3r/format.hpp"
#include "libslic3r/Model.hpp"
@@ -146,6 +147,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoAdvancedCut(m_parent, "toolbar_cut.svg", EType::Cut));
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "toolbar_support.svg", EType::FdmSupports));
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "toolbar_seam.svg", EType::Seam));
+ m_gizmos.emplace_back(new GLGizmoText(m_parent, "toolbar_text.svg", EType::Text));
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", EType::MmuSegmentation));
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
//m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++));
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
index 20584e7c4..3b03784fa 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
@@ -72,6 +72,8 @@ public:
Cut,
FdmSupports,
Seam,
+ // BBS
+ Text,
MmuSegmentation,
Simplify,
SlaSupports,