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