diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt
index 9be832c313a..3832d1401d1 100644
--- a/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt
+++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt
@@ -109,6 +109,9 @@ target_link_libraries(random_perturbation_plugin PUBLIC scene_surface_mesh_item
polyhedron_demo_plugin(degenerated_faces_plugin Degenerated_faces_plugin)
target_link_libraries(degenerated_faces_plugin PUBLIC scene_surface_mesh_item scene_selection_item)
+qt5_wrap_ui( engravUI_FILES Engrave_dock_widget.ui )
+polyhedron_demo_plugin(engrave_text_plugin Engrave_text_plugin ${engravUI_FILES})
+target_link_libraries(engrave_text_plugin PUBLIC scene_surface_mesh_item scene_selection_item scene_polylines_item)
polyhedron_demo_plugin(extrude_plugin Extrude_plugin)
target_link_libraries(extrude_plugin PUBLIC scene_surface_mesh_item scene_selection_item)
diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_dock_widget.ui b/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_dock_widget.ui
new file mode 100644
index 00000000000..a893a256196
--- /dev/null
+++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_dock_widget.ui
@@ -0,0 +1,316 @@
+
+
+ EngraveWidget
+
+
+
+ 0
+ 0
+ 293
+ 515
+
+
+
+ Engraving
+
+
+
+ -
+
+
+ An Example Text
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
-
+
+
+ Visualize
+
+
+
+ -
+
+
+ false
+
+
+ Engrave
+
+
+
+ -
+
+
+ false
+
+
+ Generate Text Mesh
+
+
+
+ -
+
+
+ Reset
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ :/cgal/icons/resources/up.png:/cgal/icons/resources/up.png
+
+
+
+ -
+
+
+ -179
+
+
+ 179
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+
+
+
+ :/cgal/icons/resources/right_arrow.png:/cgal/icons/resources/right_arrow.png
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+
+ :/cgal/icons/resources/down.png:/cgal/icons/resources/down.png
+
+
+
+ -
+
+
+ Scaling factor in X:
+
+
+
+ -
+
+
+
+
+
+
+ :/cgal/icons/resources/left_arrow.png:/cgal/icons/resources/left_arrow.png
+
+
+
+ -
+
+
+ Rotation degree:
+
+
+
+ -
+
+
+ 2000
+
+
+ 1
+
+
+ 1000
+
+
+ 1000
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2000
+
+
+ 10
+
+
+ 1000
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Scaling factor in Y:
+
+
+
+ -
+
+
+ 1
+
+
+ 15
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Text Precision
+
+
+
+
+
+ -
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Engraving Depth
+
+
+
-
+
+
+ 10000
+
+
+ 1000
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 10000
+
+
+ 1000
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Inside depth:
+
+
+
+ -
+
+
+ Outside depth:
+
+
+
+ -
+
+
+ Uniform letter mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp
new file mode 100644
index 00000000000..6cad4ed03c9
--- /dev/null
+++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp
@@ -0,0 +1,931 @@
+//General Plugin Data
+#include
+#include
+
+#include "ui_Engrave_dock_widget.h"
+//Items
+#include "Scene_surface_mesh_item.h"
+#include "Scene_polyhedron_selection_item.h"
+#include "Scene_polylines_item.h"
+#include "Messages_interface.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+using namespace CGAL::Three;
+namespace SMP = CGAL::Surface_mesh_parameterization;
+typedef EPICK::Point_2 Point_2;
+typedef EPICK::Point_3 Point_3;
+
+typedef boost::graph_traits::
+edge_descriptor edge_descriptor;
+typedef boost::graph_traits::
+halfedge_descriptor halfedge_descriptor;
+typedef boost::graph_traits::
+vertex_descriptor vertex_descriptor;
+
+typedef boost::unordered_set::
+face_descriptor> Component;
+
+struct FaceInfo2
+{
+ FaceInfo2(){}
+ int nesting_level;
+ bool in_domain(){
+ return nesting_level%2 == 1;
+ }
+};
+
+template
+struct Bot
+{
+ Bot(NMAP nmap,
+ double d,
+ PMAP pmap):d(d),
+ pmap(pmap),
+ nmap(nmap){}
+ template
+ void operator()(const T& v1,VD v2) const
+ {
+ put(pmap, v2, get(pmap, v2)-d*get(nmap, v1));
+ }
+ double d;
+ PMAP pmap;
+ NMAP nmap;
+
+};
+
+template
+struct Top
+{
+ Top(NMAP nmap,
+ PMAP pmap,
+ double d):d(d),
+ nmap(nmap),
+ pmap(pmap){}
+
+ template
+ void operator()(const T& v1, VD v2) const
+ {
+ put(pmap, v2, get(pmap, v2)+d*get(nmap, v1));
+ }
+ double d;
+ NMAP nmap;
+ PMAP pmap;
+};
+
+typedef EPICK Gt;
+typedef CGAL::Triangulation_vertex_base_2 Vb;
+typedef CGAL::Triangulation_face_base_with_info_2 Fbb;
+typedef CGAL::Constrained_triangulation_face_base_2 Fb;
+typedef CGAL::Triangulation_data_structure_2 TDS;
+typedef CGAL::No_intersection_tag Tag;
+typedef CGAL::Constrained_Delaunay_triangulation_2 CDT;
+
+//Parameterization and text displaying
+class ParamItem : public QGraphicsItem
+{
+public :
+ ParamItem(Component* component,
+ const std::vector > &polylines,
+ EPICK::Aff_transformation_2 transfo,
+ SMesh* graph,
+ QRectF brect)
+ :
+ QGraphicsItem(),
+ bounding_rect(brect),
+ component(component),
+ polylines(polylines),
+ graph(graph),
+ transfo(transfo){}
+
+ ~ParamItem()
+ {
+ delete component;
+ }
+
+ QRectF boundingRect() const
+ {
+ return bounding_rect;
+ }
+
+ void set_transfo(EPICK::Aff_transformation_2 t){ transfo = t;}
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+ {
+ QPen pen;
+ QBrush brush;
+ brush.setColor(QColor(100, 100, 255));
+ brush.setStyle(Qt::SolidPattern);
+ pen.setColor(Qt::black);
+ pen.setWidth(0);
+ painter->setPen(pen);
+ painter->setBrush(brush);
+ SMesh::Property_map > uv;
+ uv = graph->add_property_map >
+ ("h:uv",std::make_pair(0.0f,0.0f)).first;
+ for( Component::iterator
+ fi = component->begin();
+ fi != component->end();
+ ++fi)
+ {
+ boost::graph_traits::face_descriptor f(*fi);
+ QPointF points[3];
+ boost::graph_traits::halfedge_descriptor h = halfedge(f, *graph);;
+ points[0] = QPointF(get(uv, h).first, -get(uv, h).second);
+ h = next(halfedge(f, *graph), *graph);
+ points[1] = QPointF(get(uv, h).first, -get(uv, h).second);
+ h = next(next(halfedge(f, *graph), *graph), *graph);
+ points[2] = QPointF(get(uv, h).first, -get(uv, h).second);
+ painter->drawPolygon(points,3);
+ }
+
+ pen.setColor(Qt::red);
+ pen.setWidth(0);
+ painter->setPen(pen);
+ for(std::size_t i =0; i points;
+ points.reserve(polylines[i].size());
+ for(std::size_t j =0; jdrawPolyline(points.data(), static_cast(points.size()));
+ }
+ }
+
+private:
+ QString texMesh_name;
+ QRectF bounding_rect;
+ Component* component;
+ const std::vector >& polylines;
+
+ SMesh* graph;
+ EPICK::Aff_transformation_2 transfo;
+};
+
+class Navigation : public CGAL::Qt::GraphicsViewNavigation
+{
+public:
+ Navigation()
+ :CGAL::Qt::GraphicsViewNavigation(),
+ prev_pos(QPoint(0,0))
+ { }
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *ev)
+ {
+ QGraphicsView* v = qobject_cast(obj);
+ if(v == NULL) {
+ QWidget* viewport = qobject_cast(obj);
+ if(viewport == NULL) {
+ return false;
+ }
+ v = qobject_cast(viewport->parent());
+ if(v == NULL) {
+ return false;
+ }
+ }
+ switch(ev->type())
+ {
+ case QEvent::MouseMove: {
+ QMouseEvent* me = static_cast(ev);
+ if(is_dragging)
+ {
+ qreal dir[2] = {v->mapToScene(me->pos()).x() - prev_pos.x(),
+ v->mapToScene(me->pos()).y() - prev_pos.y()};
+
+ v->translate(dir[0],dir[1]);
+ v->update();
+ }
+ prev_pos = v->mapToScene(me->pos());
+ break;
+ }
+
+ case QEvent::MouseButtonPress: {
+ is_dragging = true;
+ break;
+ }
+ case QEvent::MouseButtonRelease: {
+ is_dragging = false;
+ break;
+ }
+ case QEvent::Wheel: {
+ QWheelEvent* event = static_cast(ev);
+ QPointF old_pos = v->mapToScene(event->pos());
+ if(event->delta() <0)
+ v->scale(1.2, 1.2);
+ else
+ v->scale(0.8, 0.8);
+ QPointF new_pos = v->mapToScene(event->pos());
+ QPointF delta = new_pos - old_pos;
+ v->translate(delta.x(), delta.y());
+ v->update();
+ break;
+ }
+
+ case QEvent::MouseButtonDblClick: {
+ v->fitInView(v->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
+ break;
+ }
+ default:
+ CGAL::Qt::GraphicsViewNavigation::eventFilter(obj, ev);
+ }
+ return false;
+ }
+private:
+ bool is_dragging;
+ QPointF prev_pos;
+};
+
+
+class EngraveWidget :
+ public QDockWidget,
+ public Ui::EngraveWidget
+{
+public:
+ EngraveWidget(QString name, QWidget *parent)
+ :QDockWidget(name,parent)
+ {
+ setupUi(this);
+ }
+};
+
+class Q_DECL_EXPORT Engrave_text_plugin :
+ public QObject,
+ public Polyhedron_demo_plugin_helper
+{
+ Q_OBJECT
+ Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
+ Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
+
+private:
+ typedef CGAL::Surface_mesh_shortest_path_traits SP_traits;
+ typedef CGAL::Surface_mesh_shortest_path Surface_mesh_shortest_path;
+ typedef Surface_mesh_shortest_path::Face_location Face_location;
+ typedef CGAL::AABB_face_graph_triangle_primitive Primitive;
+ typedef CGAL::AABB_traits Tree_traits;
+ typedef CGAL::AABB_tree Tree;
+ typedef EPICK::Point_3 Point_3;
+ Messages_interface* messages;
+
+public :
+
+ void init(QMainWindow*,
+ CGAL::Three::Scene_interface*,
+ Messages_interface* m) Q_DECL_OVERRIDE{
+ //get refs
+ this->scene = Three::scene();
+ this->mw = Three::mainWindow();
+ messages = m;
+
+ //action
+ QAction* actionFitText= new QAction("Fit Text", mw);
+ connect(actionFitText, SIGNAL(triggered()),
+ this, SLOT(showWidget()));
+ _actions << actionFitText;
+ //widget
+ dock_widget = new EngraveWidget("Engraving", mw);
+ dock_widget->setVisible(false); // do not show at the beginning
+ addDockWidget(dock_widget);
+ connect(dock_widget->visualizeButton, &QPushButton::clicked,
+ this, &Engrave_text_plugin::visualize);
+ connect(dock_widget->engraveButton, &QPushButton::clicked,
+ this, &Engrave_text_plugin::engrave);
+ connect(dock_widget->text_meshButton, &QPushButton::clicked,
+ this, &Engrave_text_plugin::generateTextItem);
+ connect(dock_widget->letter_checkBox, &QCheckBox::toggled,
+ this, &Engrave_text_plugin::generateTextItem);
+
+ //items
+ visu_item = nullptr;
+ sel_item = nullptr;
+ textMesh = nullptr;
+ sm = nullptr;
+
+ //transfo
+ angle = 0.0;
+ scalX=1.0;
+ scalY=1.0;
+ translation = EPICK::Vector_2(0,0);
+ pointsize = 15;
+ locked = false;
+ connect(dock_widget->text_prec_slider, &QSlider::valueChanged,
+ this, [this](){
+ pointsize = dock_widget->text_prec_slider->value();
+ scene->setSelectedItem(scene->item_id(sel_item));
+ visualize();
+ });
+ connect(dock_widget->scalX_slider, &QSlider::valueChanged,
+ this, [this](){
+ scalX = dock_widget->scalX_slider->value()/1000.0;
+ scene->setSelectedItem(scene->item_id(sel_item));
+ visualize();
+ });
+ connect(dock_widget->scalY_slider, &QSlider::valueChanged,
+ this, [this](){
+ scalY = dock_widget->scalY_slider->value()/1000.0;
+ scene->setSelectedItem(scene->item_id(sel_item));
+ visualize();
+ });
+
+ connect(dock_widget->bot_slider, &QSlider::valueChanged,
+ this, [this](){
+ if(textMesh)
+ generateTextItem();
+ });
+
+ connect(dock_widget->top_slider, &QSlider::valueChanged,
+ this, [this](){
+ if(textMesh)
+ generateTextItem();
+ });
+
+ connect(dock_widget->reset_button, &QPushButton::clicked,
+ this, [this](){
+ cleanup();
+ });
+
+ connect(dock_widget->t_up_pushButton, &QPushButton::clicked,
+ this, [this](){
+ translation += EPICK::Vector_2(0,0.005);
+ scene->setSelectedItem(scene->item_id(sel_item));
+ visualize();
+ });
+
+ connect(dock_widget->t_down_pushButton, &QPushButton::clicked,
+ this, [this](){
+ translation -= EPICK::Vector_2(0,0.005);
+ scene->setSelectedItem(scene->item_id(sel_item));
+ visualize();
+ });
+
+ connect(dock_widget->t_right_pushButton, &QPushButton::clicked,
+ this, [this](){
+ translation += EPICK::Vector_2(0.005,0);
+ scene->setSelectedItem(scene->item_id(sel_item));
+ visualize();
+ });
+
+ connect(dock_widget->t_left_pushButton, &QPushButton::clicked,
+ this, [this](){
+ translation -= EPICK::Vector_2(0.005,0);
+ scene->setSelectedItem(scene->item_id(sel_item));
+ visualize();
+ });
+ connect(dock_widget->rot_slider, &QSlider::valueChanged,
+ this, [this](){
+ if(!locked)
+ {
+ angle = dock_widget->rot_slider->value() * CGAL_PI/180.0;
+ scene->setSelectedItem(scene->item_id(sel_item));
+ visualize();
+ }
+ });
+ graphics_scene = new QGraphicsScene(dock_widget);
+ dock_widget->graphicsView->setScene(graphics_scene);
+ dock_widget->graphicsView->setRenderHints(QPainter::Antialiasing);
+ navigation = new Navigation();
+ dock_widget->graphicsView->installEventFilter(navigation);
+ dock_widget->graphicsView->viewport()->installEventFilter(navigation);
+ }
+ bool applicable(QAction*) const Q_DECL_OVERRIDE
+ {
+ return qobject_cast
+ (scene->item(scene->mainSelectionIndex()));
+ }
+ QList actions() const Q_DECL_OVERRIDE{
+ return _actions;
+ }
+public Q_SLOTS:
+ void showWidget()
+ {
+ dock_widget->setVisible(!dock_widget->isVisible());
+ }
+
+ void visualize() {
+ if(!sel_item)
+ sel_item =
+ qobject_cast
+ (scene->item(scene->mainSelectionIndex()));
+ if(!sel_item)
+ return;
+ if(sel_item->selected_facets.empty())
+ {
+ cleanup();
+ return;
+ }
+ if(!CGAL::is_closed(*sel_item->polyhedron()))
+ {
+ cleanup();
+ return;
+ }
+ if(visu_item)
+ scene->erase(scene->item_id(visu_item));
+ visu_item = nullptr;
+
+ if(!sm)
+ {
+ sm = new SMesh();
+ sel_item->export_selected_facets_as_polyhedron(sm);
+ SMesh::Halfedge_index hd =
+ CGAL::Polygon_mesh_processing::longest_border(*sm).first;
+ SMesh::Property_map uv_map =
+ sm->add_property_map("v:uv").first;
+
+ // Parameterized bool pmap
+ boost::unordered_set vs;
+ SMP::internal::Bool_property_map< boost::unordered_set > vpm(vs);
+
+ // Parameterizer
+ SMP::ARAP_parameterizer_3 parameterizer;
+
+ SMP::Error_code status = parameterizer.parameterize(*sm, hd, uv_map,
+ get(boost::vertex_index, *sm), vpm);
+ if(status != SMP::OK) {
+ std::cout << "Encountered a problem: " << status << std::endl;
+ cleanup();
+ return ;
+ }
+
+ std::cout << "Parameterized with ARAP (SM) computed." << std::endl;
+ xmin = std::numeric_limits::max();
+ xmax = std::numeric_limits::min();
+ ymin = std::numeric_limits::max();
+ ymax = std::numeric_limits::min();
+ uv_map_3 =
+ sm->add_property_map("v:uv3").first;
+ for(SMesh::Vertex_index v : sm->vertices())
+ {
+ uv_map_3[v] = Point_3(uv_map[v][0], uv_map[v]
+ [1], 0);
+ if(uv_map[v][0] > xmax)
+ xmax = uv_map[v][0];
+ if(uv_map[v][0] < xmin)
+ xmin = uv_map[v][0];
+
+ if(uv_map[v][1] > ymax)
+ ymax = uv_map[v][1];
+ if(uv_map[v][1] < ymin)
+ ymin = uv_map[v][1];
+ }
+
+ CGAL::linear_least_squares_fitting_2(
+ uv_map.begin(),
+ uv_map.end(),
+ bf_line,
+ CGAL::Dimension_tag<0>());
+
+ EPICK::Vector_2 A(bf_line.to_vector()),
+ B(EPICK::Point_2(0,0),
+ EPICK::Point_2(1,0));
+ if (A.x()<0) A=-A;
+ angle = std::acos(A.x()/CGAL::sqrt(A.squared_length()));
+ if ( A.y()<0 ) angle+=3*CGAL_PI/2.;
+ if (angle>2*CGAL_PI) angle-=2*CGAL_PI;
+
+ locked = true;
+ dock_widget->rot_slider->setSliderPosition(angle*180.0/CGAL_PI);
+ locked = false;
+ }
+ //create Text Polyline
+ QPainterPath path;
+ QFont font;
+ font.setPointSize(pointsize);
+ path.addText(QPoint(xmin,ymin), font, dock_widget->lineEdit->text());
+ QList polys = path.toSubpathPolygons();
+ polylines.clear();
+ float pxmin(8000),pxmax(-8000),
+ pymin(8000), pymax(-8000);
+
+ Q_FOREACH(QPolygonF poly, polys){
+ Q_FOREACH(QPointF pf, poly)
+ {
+ EPICK::Point_2 v = EPICK::Point_2(pf.x(),-pf.y());
+ if(v.x() < pxmin)
+ pxmin = v.x();
+ if(v.x() > pxmax)
+ pxmax = v.x();
+ if(v.y() < pymin)
+ pymin = v.y();
+ if(v.y() > pymax)
+ pymax = v.y();
+ }
+ }
+ Q_FOREACH(QPolygonF poly, polys){
+ polylines.push_back(std::vector());
+ Q_FOREACH(QPointF pf, poly)
+ {
+ EPICK::Point_2 v = EPICK::Point_2(pf.x(),-pf.y());
+ polylines.back().push_back(EPICK::Point_2(v.x()*(xmax-xmin)/(pxmax-pxmin) +xmin ,
+ v.y()*(ymax-ymin)/(pymax-pymin)+ymin
+ ));
+ }
+ }
+
+ // build AABB-tree for face location queries
+ Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3);
+
+ visu_item = new Scene_polylines_item;
+
+
+ // compute 3D coordinates
+ transfo =
+ EPICK::Aff_transformation_2(CGAL::TRANSLATION,
+ EPICK::Vector_2((xmax-xmin)/2+xmin,
+ (ymax-ymin)/2+ymin)+ translation)
+ * EPICK::Aff_transformation_2(CGAL::ROTATION,sin(angle), cos(angle))
+ * EPICK::Aff_transformation_2(scalX, 0.0,0.0,scalY)
+ * EPICK::Aff_transformation_2(CGAL::TRANSLATION,
+ EPICK::Vector_2(-(xmax-xmin)/2-xmin,
+ -(ymax-ymin)/2-ymin));
+ BOOST_FOREACH(const std::vector& polyline, polylines)
+ {
+ visu_item->polylines.push_back(std::vector());
+ BOOST_FOREACH(const EPICK::Point_2& p, polyline)
+ {
+ EPICK::Point_2 p_2 = transfo.transform(p);
+
+ Face_location loc = Surface_mesh_shortest_path::locate(
+ Point_3(p_2.x(), p_2.y(), 0),
+ aabb_tree, *sm, uv_map_3);
+ visu_item->polylines.back().push_back(
+ Surface_mesh_shortest_path::point(loc.first, loc.second, *sm, sm->points()));
+ }
+ }
+ visu_item->setName("Text");
+ visu_item->setColor(QColor(Qt::red));
+ scene->addItem(visu_item);
+ dock_widget->engraveButton->setEnabled(true);
+ dock_widget->text_meshButton->setEnabled(true);
+
+ if(graphics_scene->items().empty())
+ {
+ Component* component = new Component();
+ face_iterator bfit;
+ for(bfit = faces(*sm).begin();
+ bfit != faces(*sm).end();
+ ++bfit)
+ {
+ component->insert(*bfit);
+ }
+ SMesh::Property_map > uv;
+ uv = sm->add_property_map >(
+ "h:uv",std::make_pair(0.0f,0.0f)).first;
+ SMesh::Halfedge_iterator it;
+ SMesh::Property_map uv_map =
+ sm->property_map("v:uv").first;
+ for(it = sm->halfedges_begin();
+ it != sm->halfedges_end();
+ ++it)
+ {
+ halfedge_descriptor hd(*it);
+ EPICK::FT u = uv_map[target(hd, *sm)].x();
+ EPICK::FT v = uv_map[target(hd, *sm)].y();
+ put(uv, *it, std::make_pair(static_cast(u),static_cast(v)));
+ }
+
+ //ParamItem does not take ownership of text_mesh_bottom
+ ParamItem *param_item= new ParamItem(component, polylines, transfo, sm,
+ QRectF(QPointF(xmin, -ymax), QPointF(xmax, -ymin)));
+ graphics_scene->addItem(param_item);
+ dock_widget->graphicsView->fitInView(param_item->boundingRect(), Qt::KeepAspectRatio);
+ }
+ else
+ {
+ ParamItem* param_item = static_cast(graphics_scene->items().first());
+ param_item->set_transfo(transfo);
+ dock_widget->graphicsView->activateWindow();
+ graphics_scene->update();
+ }
+ // dock_widget->visualizeButton->setEnabled(false);
+ }
+
+ void create_text_mesh(SMesh& text_mesh)
+ {
+ if(!visu_item)
+ return;
+ if(!sel_item)
+ return;
+ if(sel_item->selected_facets.empty())
+ return;
+ if(!CGAL::is_closed(*sel_item->polyhedron()))
+ return;
+ CDT cdt;
+ //polylines is duplicated so the transformation is only performed once
+ std::vector > local_polylines
+ = polylines;
+ try{
+ for(std::size_t i = 0;
+ i < local_polylines.size(); ++i)
+ {
+ std::vector& points = local_polylines[i];
+ for(std::size_t j = 0; j< points.size(); ++j)
+ {
+ Point_2 &p = points[j];
+ p = transfo.transform(p);
+ }
+ cdt.insert_constraint(points.begin(),points.end());
+ }
+ }catch(std::runtime_error&)
+ {
+ QApplication::restoreOverrideCursor();
+ throw;
+ }
+ if (cdt.dimension()!=2){
+ QApplication::restoreOverrideCursor();
+ std::cout << "Triangulation is not of dimension 2" << std::endl;
+ return;
+ }
+ CGAL::Bbox_2 bbox= CGAL::bbox_2(local_polylines.front().begin(),
+ local_polylines.front().end(),
+ EPICK());
+ Q_FOREACH(const std::vector& points,
+ local_polylines)
+ {
+ bbox += CGAL::bbox_2(points.begin(), points.end(), EPICK());
+ }
+ mark_nested_domains(cdt);
+
+ SMesh text_mesh_bottom;
+ cdt2_to_face_graph(cdt,
+ text_mesh_bottom);
+ typedef boost::property_map::type VPMap;
+ typedef SMesh::Property_map NPMAP;
+ NPMAP vnormals =
+ text_mesh_bottom.add_property_map("v:normal").first;
+
+ if(!dock_widget->letter_checkBox->isChecked())
+ {
+ CGAL::Polygon_mesh_processing::compute_vertex_normals(text_mesh_bottom, vnormals);
+ }
+ else{
+ // \todo Computing normals before the final
+ // mesh would be better.
+
+ //foreach CC
+ SMesh::Property_map fcmap =
+ text_mesh_bottom.add_property_map("f:cc", 0).first;
+ std::size_t nb_cc = PMP::connected_components(text_mesh_bottom,
+ fcmap);
+ for(std::size_t cc = 0; cc fmesh(text_mesh_bottom,
+ static_cast(cc),
+ fcmap);
+ BOOST_FOREACH(vertex_descriptor vd, vertices(fmesh))
+ {
+ normal += CGAL::Polygon_mesh_processing::compute_vertex_normal(vd, fmesh);
+ }
+ normal /= CGAL::sqrt(normal.squared_length());
+ BOOST_FOREACH(vertex_descriptor vd, vertices(fmesh))
+ {
+ put(vnormals, vd, normal);
+ }
+ }
+ }
+ Bot bot(vnormals, dock_widget->bot_slider->value()/100000.0,
+ get(CGAL::vertex_point, text_mesh));
+ Top top(vnormals, get(CGAL::vertex_point, text_mesh),
+ dock_widget->top_slider->value()/100000.0);
+ PMP::extrude_mesh(text_mesh_bottom, text_mesh, bot, top);
+ }
+
+ void engrave() {
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ SMesh text_mesh_complete;
+ create_text_mesh(text_mesh_complete);
+
+ if (PMP::does_self_intersect(text_mesh_complete))
+ {
+ QApplication::restoreOverrideCursor();
+ messages->information("Error: text mesh self-intersects!");
+ return;
+ }
+
+ SMesh result;
+ CGAL::copy_face_graph(*sel_item->polyhedron(), result);
+ bool OK = PMP::corefine_and_compute_difference(result, text_mesh_complete, result);
+
+ if (!OK)
+ {
+ QApplication::restoreOverrideCursor();
+ messages->information("Error: the output mesh is not manifold!");
+ return;
+ }
+
+ CGAL::Polygon_mesh_processing::triangulate_faces(result);
+ Scene_surface_mesh_item* result_item = new Scene_surface_mesh_item(
+ result);
+ scene->addItem(result_item);
+ graphics_scene->clear();
+ cleanup();
+ if(textMesh)
+ {
+ textMesh->setVisible(false);
+ }
+ QApplication::restoreOverrideCursor();
+ dock_widget->engraveButton->setEnabled(false);
+ dock_widget->text_meshButton->setEnabled(false);
+ dock_widget->visualizeButton->setEnabled(true);
+ }
+
+ void generateTextItem()
+ {
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+
+ if(textMesh)
+ {
+ textMesh->face_graph()->clear();
+ create_text_mesh(*textMesh->face_graph());
+ textMesh->invalidateOpenGLBuffers();
+ }
+ else
+ {
+ SMesh text_mesh;
+ create_text_mesh(text_mesh);
+ textMesh = new Scene_surface_mesh_item(text_mesh);
+ connect(textMesh, &Scene_surface_mesh_item::aboutToBeDestroyed,
+ this, [this](){
+ textMesh = nullptr;});
+ textMesh->setName("Extruded Text");
+ scene->addItem(textMesh);
+ }
+ QApplication::restoreOverrideCursor();
+ }
+
+ void closure()Q_DECL_OVERRIDE
+ {
+ dock_widget->hide();
+ }
+
+private:
+ template
+ void
+ mark_domains(CDT& ct,
+ typename CDT::Face_handle start,
+ int index,
+ std::list& border )
+ {
+ if(start->info().nesting_level != -1){
+ return;
+ }
+ std::list queue;
+ queue.push_back(start);
+ while(! queue.empty()){
+ typename CDT::Face_handle fh = queue.front();
+ queue.pop_front();
+ if(fh->info().nesting_level == -1){
+ fh->info().nesting_level = index;
+ for(int i = 0; i < 3; i++){
+ typename CDT::Edge e(fh,i);
+ typename CDT::Face_handle n = fh->neighbor(i);
+ if(n->info().nesting_level == -1){
+ if(ct.is_constrained(e)) border.push_back(e);
+ else queue.push_back(n);
+ }
+ }
+ }
+ }
+ }
+
+
+ template
+ void
+ mark_nested_domains(CDT& cdt)
+ {
+ for(typename CDT::All_faces_iterator it = cdt.all_faces_begin(); it !=
+ cdt.all_faces_end(); ++it){
+ it->info().nesting_level = -1;
+ }
+ std::list border;
+ mark_domains(cdt, cdt.infinite_face(), 0, border);
+ while(! border.empty()){
+ typename CDT::Edge e = border.front();
+ border.pop_front();
+ typename CDT::Face_handle n = e.first->neighbor(e.second);
+ if(n->info().nesting_level == -1){
+ mark_domains(cdt, n, e.first->info().nesting_level+1, border);
+ }
+ }
+ }
+
+ template
+ void cdt2_to_face_graph(const CDT& cdt,
+ TriangleMesh& tm)
+ {
+
+ Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3);
+ typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor;
+
+ typedef std::map Map;
+ Map descriptors;
+ for (typename CDT::Finite_faces_iterator fit=cdt.finite_faces_begin(),
+ fit_end=cdt.finite_faces_end();
+ fit!=fit_end; ++fit)
+ {
+ if (!fit->info().in_domain()) continue;
+ CGAL::cpp11::array vds;
+ for(int i=0; i<3; ++i)
+ {
+ typename Map::iterator it;
+ bool insert_ok;
+ boost::tie(it,insert_ok) =
+ descriptors.insert(std::make_pair(fit->vertex(i),vertex_descriptor()));
+ if (insert_ok){
+ const EPICK::Point_2& pt=fit->vertex(i)->point();
+ Face_location loc = Surface_mesh_shortest_path::locate(
+ Point_3(pt.x(), pt.y(), 0),
+ aabb_tree, *sm, uv_map_3);
+ it->second = add_vertex(Surface_mesh_shortest_path::point(loc.first, loc.second,
+ *sm, sm->points()), tm);
+ }
+ vds[i]=it->second;
+ }
+
+ CGAL::Euler::add_face(vds, tm);
+ }
+ }
+
+ void cleanup()
+ {
+ dock_widget->scalX_slider->setValue(1000);
+ dock_widget->scalY_slider->setValue(1000);
+ dock_widget->rot_slider->setValue(0);
+ translation = EPICK::Vector_2(0,0);
+ uv_map_3.reset();
+ graphics_scene->clear();
+ if(sel_item)
+ {
+ scene->erase(scene->item_id(sel_item));
+ sel_item =nullptr;
+ }
+ if(sm)
+ {
+ delete sm;
+ sm = nullptr;
+ }
+ if(visu_item)
+ {
+ scene->erase(scene->item_id(visu_item));
+ visu_item = nullptr;
+ }
+ }
+
+ QList _actions;
+ EngraveWidget* dock_widget;
+ Scene_polylines_item* visu_item;
+ Scene_polyhedron_selection_item* sel_item;
+ Scene_surface_mesh_item* textMesh;
+ double angle;
+ double scalX;
+ double scalY;
+ EPICK::Vector_2 translation;
+ EPICK::Aff_transformation_2 transfo;
+ std::vector > polylines;
+ SMesh::Property_map uv_map_3;
+ SMesh* sm;
+ float xmin, xmax, ymin, ymax;
+ int pointsize;
+ bool locked;
+ EPICK::Line_2 bf_line;
+ QGraphicsScene *graphics_scene;
+ Navigation* navigation;
+};
+#include "Engrave_text_plugin.moc"
+