1 #include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
2 #include <CGAL/Three/Three.h>
3 #include <QApplication>
4 #include <QObject>
5 #include <QAction>
6 #include <QMainWindow>
7 #include <QInputDialog>
8 #include <QMessageBox>
9 
10 #include <CGAL/Three/Three.h>
11 #include <CGAL/Three/Scene_item_rendering_helper.h>
12 #include <CGAL/Three/Viewer_interface.h>
13 #include <CGAL/Three/Triangle_container.h>
14 
15 #include <CGAL/linear_least_squares_fitting_3.h>
16 #include <CGAL/Polygon_mesh_processing/extrude.h>
17 #include <CGAL/Polygon_mesh_processing/compute_normal.h>
18 
19 #include <CGAL/Qt/manipulatedFrame.h>
20 #include <CGAL/Qt/constraint.h>
21 
22 #include <CGAL/number_type_config.h>
23 
24 #include "Messages_interface.h"
25 #include "Kernel_type.h"
26 #include "Scene_surface_mesh_item.h"
27 #include "Scene_polyhedron_selection_item.h"
28 #include "Scene.h"
29 typedef Scene_surface_mesh_item Scene_face_graph_item;
30 typedef Scene_face_graph_item::Face_graph Face_graph;
31 typedef CGAL::qglviewer::Vec Vec;
32 using namespace CGAL::Three;
33 typedef Viewer_interface Vi;
34 typedef Triangle_container Tc;
35 //use frame to get dist and dir.
36 //fix frame in translation and use mousewheel to choose dist
37 //finding frame's position : first try at the center of the item's bbox
38 //maybe find intersection between surface and diag bbox.
39 //orientation : PCA : normal.
40 typedef Kernel::Plane_3 Plane;
41 typedef Kernel::Triangle_3 Triangle;
42 typedef Kernel::Point_3 Point;
43 typedef Kernel::Vector_3 Vector;
44 class Scene_arrow_item : public Scene_item_rendering_helper
45 {
46   Q_OBJECT
47 public :
Scene_arrow_item(Vec center_,double r,double length_)48   Scene_arrow_item(Vec center_, double r, double length_)
49     :center_(center_), length_(length_), R(r),
50        frame(new Scene_item::ManipulatedFrame())
51   {
52     const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset();
53         frame->setPosition( center_+offset);
54     tick = length_/10.0f;
55     ctrl_pressing = false;
56     setTriangleContainer(0, new Tc(Vi::PROGRAM_WITH_LIGHT, false));
57   }
58   // Indicates if rendering mode is supported
supportsRenderingMode(RenderingMode m) const59   bool supportsRenderingMode(RenderingMode m) const Q_DECL_OVERRIDE {
60     return (m == Gouraud);
61   }
62   //Displays the item
draw(Viewer_interface * viewer) const63   void draw(Viewer_interface* viewer) const Q_DECL_OVERRIDE
64   {
65     if(!isInit(viewer))
66       initGL(viewer);
67     if (! getBuffersInit(viewer))
68     {
69       initializeBuffers(viewer);
70       setBuffersInit(viewer, true);
71     }
72     GLdouble d_mat[16];
73     GLdouble matrix[16];
74     QMatrix4x4 f_matrix;
75     frame->getMatrix(matrix);
76     for (int i=0; i<16; ++i)
77       f_matrix.data()[i] = (float)matrix[i];
78     viewer->camera()->getModelViewProjectionMatrix(d_mat);
79     QMatrix4x4 mv_mat;
80     viewer->camera()->getModelViewMatrix(d_mat);
81     for (int i=0; i<16; ++i)
82       mv_mat.data()[i] = GLfloat(d_mat[i]);
83     mv_mat = mv_mat*f_matrix;
84 
85     Tc* tc = getTriangleContainer(0);
86     tc->setMvMatrix(mv_mat);
87     tc->setFrameMatrix(f_matrix);
88     tc->setClipping(false);
89     tc->setColor(this->color());
90     tc->draw(viewer, true);
91   }
invalidateOpenGLBuffers()92   void invalidateOpenGLBuffers() Q_DECL_OVERRIDE
93   {
94     Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool())
95     {
96       CGAL::Three::Viewer_interface* viewer =
97           static_cast<CGAL::Three::Viewer_interface*>(v);
98       if(viewer == NULL)
99         continue;
100       setBuffersInit(viewer, false);
101     }
102     getTriangleContainer(0)->reset_vbos(ALL);
103   }
compute_bbox() const104   void compute_bbox() const Q_DECL_OVERRIDE {}
clone() const105   Scene_item* clone() const Q_DECL_OVERRIDE {return 0;}
toolTip() const106   QString toolTip() const Q_DECL_OVERRIDE {return QString();}
center() const107   Vec center()const { return center_; }
manipulatedFrame()108   Scene_item::ManipulatedFrame* manipulatedFrame() Q_DECL_OVERRIDE { return frame; }
manipulatable() const109   bool manipulatable() const Q_DECL_OVERRIDE { return true; }
eventFilter(QObject *,QEvent * event)110   bool eventFilter(QObject *, QEvent *event) Q_DECL_OVERRIDE
111   {
112     if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
113     {
114       ctrl_pressing = static_cast<QKeyEvent*>(event)->modifiers().testFlag(Qt::ControlModifier);
115     }
116     if(event->type() == QEvent::Wheel && ctrl_pressing)
117     {
118       QWheelEvent *mouseEvent = static_cast<QWheelEvent*>(event);
119       int steps = mouseEvent->angleDelta().y() / 120;
120       if (steps > 0)
121         length_+=tick;
122       else
123         length_-=tick;
124       invalidateOpenGLBuffers();
125       redraw();
126       return true;
127     }
128     return false;
129   }
length() const130   double length()const { return length_; }
131 private:
132   //make an arrow showing the length and direction of the transformation for the extrusion.
initializeBuffers(Viewer_interface * viewer) const133   void initializeBuffers(Viewer_interface *viewer)const Q_DECL_OVERRIDE
134   {
135     std::vector<float> vertices;
136     std::vector<float> normals;
137     int prec = 60;
138     //Head
139     const float Rf = static_cast<float>(R);
140     for(int d = 0; d<360; d+= 360/prec)
141     {
142       float D = (float) (d * CGAL_PI / 180.);
143       float a = (float) std::atan(Rf / 0.33);
144       QVector4D pR(0., 1.*length_, 0, 1.);
145       QVector4D nR(Rf*2.*sin(D), sin(a), Rf*2.*cos(D), 1.);
146 
147       //point A1
148       vertices.push_back(pR.x());
149       vertices.push_back(pR.y());
150       vertices.push_back(pR.z());
151       normals.push_back(nR.x());
152       normals.push_back(nR.y());
153       normals.push_back(nR.z());
154 
155       //point B1
156       pR = QVector4D(Rf*2.*sin(D), 0.66f*length_, Rf*2.* cos(D), 1.f);
157       nR = QVector4D(sin(D), sin(a), cos(D), 1.);
158       vertices.push_back(pR.x());
159       vertices.push_back(pR.y());
160       vertices.push_back(pR.z());
161       normals.push_back(nR.x());
162       normals.push_back(nR.y());
163       normals.push_back(nR.z());
164       //point C1
165       D = (d+360/prec)*CGAL_PI/180.0;
166       pR = QVector4D(Rf*2.* sin(D), 0.66f*length_, Rf *2.* cos(D), 1.f);
167       nR = QVector4D(sin(D), sin(a), cos(D), 1.0);
168 
169       vertices.push_back(pR.x());
170       vertices.push_back(pR.y());
171       vertices.push_back(pR.z());
172       normals.push_back(nR.x());
173       normals.push_back(nR.y());
174       normals.push_back(nR.z());
175     }
176 
177     //cylinder
178     //body of the cylinder
179     for(int d = 0; d<360; d+= 360/prec)
180     {
181       //point A1
182       double D = d*CGAL_PI/180.0;
183       QVector4D pR(Rf*sin(D), 0.66f*length_, Rf*cos(D), 1.f);
184       QVector4D nR(sin(D), 0.f, cos(D), 1.f);
185 
186       vertices.push_back(pR.x());
187       vertices.push_back(pR.y());
188       vertices.push_back(pR.z());
189       normals.push_back(nR.x());
190       normals.push_back(nR.y());
191       normals.push_back(nR.z());
192       //point B1
193       pR = QVector4D(Rf * sin(D),0,Rf*cos(D), 1.0);
194       nR = QVector4D(sin(D), 0, cos(D), 1.0);
195 
196       vertices.push_back(pR.x());
197       vertices.push_back(pR.y());
198       vertices.push_back(pR.z());
199       normals.push_back(nR.x());
200       normals.push_back(nR.y());
201       normals.push_back(nR.z());
202       //point C1
203       D = (d+360/prec)*CGAL_PI/180.0;
204       pR = QVector4D(Rf * sin(D),0,Rf*cos(D), 1.0);
205       nR = QVector4D(sin(D), 0, cos(D), 1.0);
206       vertices.push_back(pR.x());
207       vertices.push_back(pR.y());
208       vertices.push_back(pR.z());
209       normals.push_back(nR.x());
210       normals.push_back(nR.y());
211       normals.push_back(nR.z());
212       //point A2
213       D = (d+360/prec)*CGAL_PI/180.0;
214 
215       pR = QVector4D(Rf * sin(D),0,Rf*cos(D), 1.0);
216       nR = QVector4D(sin(D), 0, cos(D), 1.0);
217 
218       vertices.push_back(pR.x());
219       vertices.push_back(pR.y());
220       vertices.push_back(pR.z());
221       normals.push_back(nR.x());
222       normals.push_back(nR.y());
223       normals.push_back(nR.z());
224       //point B2
225       pR = QVector4D(Rf * sin(D), 0.66f*length_, Rf*cos(D), 1.f);
226       nR = QVector4D(sin(D), 0, cos(D), 1.0);
227 
228       vertices.push_back(pR.x());
229       vertices.push_back(pR.y());
230       vertices.push_back(pR.z());
231       normals.push_back(nR.x());
232       normals.push_back(nR.y());
233       normals.push_back(nR.z());
234       //point C2
235       D = d*CGAL_PI/180.0;
236       pR = QVector4D(Rf * sin(D), 0.66f*length_, Rf*cos(D), 1.f);
237       nR = QVector4D(sin(D), 0.f, cos(D), 1.f);
238 
239       vertices.push_back(pR.x());
240       vertices.push_back(pR.y());
241       vertices.push_back(pR.z());
242       normals.push_back(nR.x());
243       normals.push_back(nR.y());
244       normals.push_back(nR.z());
245     }
246 
247     //fill buffers
248     //vao containing the data for the facets
249     Tc* tc = getTriangleContainer(0);
250     tc->allocate(Tc::Flat_vertices,
251                  vertices.data(),
252                  static_cast<GLsizei>(vertices.size()*sizeof(float)));
253 
254     tc->allocate(Tc::Flat_normals,
255                  normals.data(),
256                  static_cast<GLsizei>(normals.size()*sizeof(float)));
257     tc->initializeBuffers(viewer);
258     tc->setFlatDataSize(vertices.size());
259 
260     _bbox = Bbox(0,0,0,0,0,0);
261     for(std::size_t i = 0; i< vertices.size(); i+=3)
262     {
263       _bbox += Point(vertices[i],
264                      vertices[i+1],
265                      vertices[i+2]).bbox();
266     }
267     setBbox(_bbox);
268     setBuffersInit(viewer, true);
269   }
270 
271   Vec center_;
272   double length_;
273   double tick;
274   double R;
275   bool ctrl_pressing;
276   Scene_item::ManipulatedFrame* frame;
277 }; //end of class Scene_arrow_item
278 
279 
280 template <typename TriangleMesh, typename OutputIterator>
triangles(const TriangleMesh & mesh,OutputIterator out)281 CGAL::Bbox_3 triangles(const TriangleMesh& mesh,
282                          OutputIterator out)
283 {
284   CGAL::Bbox_3 bb;
285   typename boost::property_map<TriangleMesh,CGAL::vertex_point_t>::const_type vpm =
286       get(CGAL::vertex_point, mesh);
287   for(typename boost::graph_traits<TriangleMesh>::face_descriptor fd : faces(mesh)){
288     typename boost::graph_traits<TriangleMesh>::halfedge_descriptor hd = halfedge(fd,mesh);
289     Triangle t(get(vpm,source(hd,mesh)),
290                get(vpm,target(hd,mesh)),
291                get(vpm,target(next(hd,mesh),mesh)));
292     *out++ = t;
293     bb = bb + t.bbox();
294   }
295   return bb;
296 }
297 
estimate_normals(const std::vector<Triangle> & tris)298 Vector estimate_normals(const std::vector<Triangle>& tris)
299 {
300   Vector moy(0,0,0);
301 
302   for(const Triangle& tri : tris)
303   {
304     Vector norm = CGAL::Polygon_mesh_processing::internal::triangle_normal(
305           tri[0], tri[1], tri[2], Kernel());
306     norm /= CGAL::sqrt(norm.squared_length());
307     moy += norm;
308   }
309   return moy;
310 }
311 
312 
313 class ExtrudePlugin :
314     public QObject,
315     public Polyhedron_demo_plugin_interface
316 {
317   Q_OBJECT
318   Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
319   Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0" FILE "extrude_plugin.json")
320 public:
321 
applicable(QAction * action) const322   bool applicable(QAction* action) const Q_DECL_OVERRIDE
323   {
324     if(action == actionCreateItem)
325     {
326       return !oliver_queen &&
327           (qobject_cast<Scene_face_graph_item*>(scene->item(scene->mainSelectionIndex()))
328            || qobject_cast<Scene_polyhedron_selection_item*>(scene->item(scene->mainSelectionIndex())));
329     }
330     else if(oliver_queen)
331       return true;
332     return false;
333   }
334 
actions() const335   QList<QAction*> actions() const Q_DECL_OVERRIDE
336   {
337     return _actions;
338   }
339 
init(QMainWindow * mainWindow,Scene_interface * sc,Messages_interface * mi)340   void init(QMainWindow* mainWindow, Scene_interface* sc, Messages_interface* mi) Q_DECL_OVERRIDE
341   {
342     this->messageInterface = mi;
343     this->scene = sc;
344     this->mw = mainWindow;
345     oliver_queen = NULL;
346     target = NULL;
347     actionCreateItem = new QAction(QString("Extrude FaceGraph (or selection)"), mw);
348     actionCreateItem->setProperty("submenuName", "Polygon Mesh Processing");
349     connect(actionCreateItem, SIGNAL(triggered()),
350             this, SLOT(createItem()));
351     _actions << actionCreateItem;
352     actionExtrude = new QAction(QString("Perform Extrusion"), mw);
353     actionExtrude->setProperty("submenuName", "Polygon Mesh Processing");
354     connect(actionExtrude, SIGNAL(triggered()),
355             this, SLOT(do_extrude()));
356     _actions << actionExtrude;
357     connect(mw, SIGNAL(newViewerCreated(QObject*)),
358             this, SLOT(connectNewViewer(QObject*)));
359   }
360 private Q_SLOTS:
connectNewViewer(QObject * o)361   void connectNewViewer(QObject* o)
362   {
363     for(int i=0; i<scene->numberOfEntries(); ++i)
364     {
365       if(oliver_queen)
366         o->installEventFilter(oliver_queen);
367     }
368   }
createItem()369   void createItem()
370   {
371     Scene_item * item = scene->item(scene->mainSelectionIndex());
372     Scene_polyhedron_selection_item* sel_item = qobject_cast<Scene_polyhedron_selection_item*>(scene->item(scene->mainSelectionIndex()));
373     Scene_face_graph_item* fg_item = qobject_cast<Scene_face_graph_item*>(item);
374     Face_graph* pMesh = NULL;
375     if(sel_item)
376     {
377       pMesh = new Face_graph();
378       if(!sel_item->export_selected_facets_as_polyhedron(pMesh))
379       {
380         CGAL::Three::Three::error("Face selection is not valid. Aborting.");
381 
382         return;
383       }
384       fg_item = new Scene_facegraph_item(pMesh);
385       fg_item->setName(QString("%1 selection").arg(sel_item->polyhedron_item()->name()));
386       scene->addItem(fg_item);
387       sel_item->polyhedron_item()->setWireframeMode();
388       sel_item->polyhedron_item()->redraw();
389     }
390     if(fg_item)
391       pMesh = fg_item->face_graph();
392     else
393       return;
394     if(CGAL::is_closed(*pMesh))
395     {
396       CGAL::Three::Three::error("The face graph must be open. Aborting.");
397       return;
398     }
399     std::vector<Triangle> triangles;
400     ::triangles(*pMesh,std::back_inserter(triangles));
401     Plane plane;
402     CGAL::linear_least_squares_fitting_3(triangles.begin(),triangles.end(),plane,CGAL::Dimension_tag<2>());
403 
404     // compute centroid
405     Point c = CGAL::centroid(triangles.begin(),triangles.end());
406 
407     oliver_queen = new Scene_arrow_item(Vec(c.x(),c.y(),c.z()), fg_item->diagonalBbox() / 50.0f,
408                                         fg_item->diagonalBbox()/3.0f);
409     Vec dir(plane.orthogonal_vector().x(),
410             plane.orthogonal_vector().y(),
411             plane.orthogonal_vector().z());
412     if(CGAL::scalar_product(Vector(dir.x, dir.y, dir.z), estimate_normals(triangles)) > 0)
413       dir = -dir;
414 
415     CGAL::qglviewer::Quaternion orientation(CGAL::qglviewer::Vec(0,1,0), dir);
416     oliver_queen->manipulatedFrame()->setOrientation(orientation);
417     constraint.setRotationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE);
418     constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FORBIDDEN);
419     oliver_queen->manipulatedFrame()->setConstraint(&constraint);
420     oliver_queen->setColor(QColor(Qt::green));
421     oliver_queen->setName("Extrude item");
422     Q_FOREACH(CGAL::QGLViewer* viewer, CGAL::QGLViewer::QGLViewerPool())
423       viewer->installEventFilter(oliver_queen);
424     mw->installEventFilter(oliver_queen);
425     scene->addItem(oliver_queen);
426     target = fg_item;
427 
428     connect(oliver_queen, &Scene_arrow_item::aboutToBeDestroyed,
429             [this](){
430       oliver_queen = NULL;
431     });
432 
433     //!@todo : add a way to track scene's bbox recomputation and reset frame's position when triggered.
434   }
do_extrude()435   void do_extrude()
436   {
437     if(!target)
438       return;
439     Face_graph pMesh = *target->face_graph();
440     target->face_graph()->clear();
441     double length = oliver_queen->length();
442     double matrix[16];
443     oliver_queen->manipulatedFrame()->getMatrix(matrix);
444     QMatrix4x4 rotate_matrix;
445     QMatrix4x4 transform_matrix;
446     for(int i=0; i<16; ++i)
447       transform_matrix.data()[i] = (float)matrix[i];
448     rotate_matrix = transform_matrix;
449     rotate_matrix.setColumn(3, QVector4D(0,0,0,1));
450     QVector3D dir = rotate_matrix * QVector3D(0,1,0);
451     dir.normalize();
452     dir = length * dir;
453 
454     CGAL::Polygon_mesh_processing::extrude_mesh(pMesh, *target->face_graph(),
455                                                 Kernel::Vector_3(dir.x(), dir.y(), dir.z()));
456     scene->erase(scene->item_id(oliver_queen));
457     oliver_queen = NULL;
458     target->resetColors();
459     target->invalidateOpenGLBuffers();
460     target->itemChanged();
461     target = NULL;
462   }
463 
464 private:
465 
466   QList<QAction*> _actions;
467   Messages_interface* messageInterface;
468   Scene_interface* scene;
469   QMainWindow* mw;
470   QAction *actionCreateItem;
471   QAction *actionExtrude;
472   Scene_arrow_item* oliver_queen;
473   Scene_face_graph_item* target;
474   CGAL::qglviewer::LocalConstraint constraint;
475 };
476 #include "Extrude_plugin.moc"
477