1 #include "config.h"
2 #include "Scene.h"
3 
4 #include <CGAL/Three/Scene_item.h>
5 #include <CGAL/Three/Scene_print_item_interface.h>
6 #include <CGAL/Three/Scene_transparent_interface.h>
7 #include <CGAL/Three/Scene_zoomable_item_interface.h>
8 #include <CGAL/Three/Three.h>
9 
10 #include  <CGAL/Three/Scene_item.h>
11 #include <CGAL/Three/Scene_print_item_interface.h>
12 #include <CGAL/Three/Viewer_interface.h>
13 
14 
15 #include <QObject>
16 #include <QMetaObject>
17 #include <QString>
18 #include <QGLWidget>
19 #include <QEvent>
20 #include <QMouseEvent>
21 #include <QPainter>
22 #include <QColorDialog>
23 #include <QApplication>
24 #include <QPointer>
25 #include <QList>
26 #include <QAbstractProxyModel>
27 #include <QMimeData>
28 #include <QOpenGLFramebufferObject>
29 
30 
Scene(QObject * parent)31 Scene::Scene(QObject* parent)
32     : QStandardItemModel(parent),
33       selected_item(-1),
34       item_A(-1),
35       item_B(-1)
36 {
37 
38     connect(this, SIGNAL(selectionRay(double, double, double,
39                                       double, double, double)),
40             this, SLOT(setSelectionRay(double, double, double,
41                                        double, double, double)));
42     connect(this, SIGNAL(indexErased(Scene_interface::Item_id)),
43               this, SLOT(adjustIds(Scene_interface::Item_id)));
44     picked = false;
45     gl_init = false;
46     dont_emit_changes = false;
47 
48 }
49 Scene::Item_id
addItem(CGAL::Three::Scene_item * item)50 Scene::addItem(CGAL::Three::Scene_item* item)
51 {
52     Bbox bbox_before = bbox();
53     m_entries.push_back(item);
54     Item_id id = m_entries.size() - 1;
55     connect(item, SIGNAL(itemChanged()),
56             this, SLOT(itemChanged()));
57     connect(item, SIGNAL(itemVisibilityChanged()),
58             this, SLOT(itemVisibilityChanged()));
59     connect(item, SIGNAL(redraw()),
60             this, SLOT(callDraw()));
61     if(item->isFinite()
62             && !item->isEmpty()
63             && bbox_before + item->bbox() != bbox_before
64             )
65     {
66         Q_EMIT updated_bbox(true);
67     }
68     QList<QStandardItem*> list;
69     for(int i=0; i<5; i++)
70     {
71         list<<new QStandardItem();
72         list.at(i)->setEditable(false);
73     }
74     invisibleRootItem()->appendRow(list);
75     for(int i=0; i<5; i++){
76         index_map[list.at(i)->index()] = m_entries.size() -1;
77     }
78     Q_EMIT updated();
79     children.push_back(id);
80     Q_EMIT newItem(id);
81     CGAL::Three::Scene_group_item* group =
82             qobject_cast<CGAL::Three::Scene_group_item*>(item);
83     if(group)
84         addGroup(group);
85     //init the item for the mainViewer to avoid using unexisting
86     //VAOs if the mainViewer is not the first to be drawn.
87     QOpenGLFramebufferObject* fbo = CGAL::Three::Three::mainViewer()->depthPeelingFbo();
88     CGAL::Three::Three::mainViewer()->setDepthPeelingFbo(nullptr);//to prevent crashing as the fbo is not initialized in this call.
89     item->draw(CGAL::Three::Three::mainViewer());
90     item->drawEdges(CGAL::Three::Three::mainViewer());
91     item->drawPoints(CGAL::Three::Three::mainViewer());
92     CGAL::Three::Three::mainViewer()->setDepthPeelingFbo(fbo);
93     if(group)
94        m_groups.append(id);
95     return id;
96 }
97 
98 CGAL::Three::Scene_item*
replaceItem(Scene::Item_id index,CGAL::Three::Scene_item * item,bool emit_item_about_to_be_destroyed)99 Scene::replaceItem(Scene::Item_id index, CGAL::Three::Scene_item* item, bool emit_item_about_to_be_destroyed)
100 {
101     if(index < 0 || index >= m_entries.size())
102         return nullptr;
103 
104     connect(item, SIGNAL(itemChanged()),
105             this, SLOT(itemChanged()));
106     connect(item, SIGNAL(itemVisibilityChanged()),
107             this, SLOT(itemVisibilityChanged()));
108     connect(item, SIGNAL(redraw()),
109             this, SLOT(callDraw()));
110     CGAL::Three::Scene_group_item* group =
111             qobject_cast<CGAL::Three::Scene_group_item*>(m_entries[index]);
112     QList<Scene_item*> group_children;
113     if(group)
114     {
115       m_groups.removeAll(index);
116       Q_FOREACH(Item_id id, group->getChildren())
117       {
118         CGAL::Three::Scene_item* child = group->getChild(id);
119         group->unlockChild(child);
120         group_children << child;
121       }
122     }
123     CGAL::Three::Scene_group_item* parent = m_entries[index]->parentGroup();
124     bool is_locked = false;
125     if(parent)
126     {
127       is_locked = parent->isChildLocked(m_entries[index]);
128       parent->unlockChild(m_entries[index]);
129       parent->removeChild(m_entries[index]);
130     }
131     std::swap(m_entries[index], item);
132     if(parent)
133     {
134       changeGroup(m_entries[index], parent);
135       if(is_locked)
136         parent->lockChild(m_entries[index]);
137     }
138 
139     Q_EMIT newItem(index);
140     if ( item->isFinite() && !item->isEmpty() &&
141          m_entries[index]->isFinite() && !m_entries[index]->isEmpty() &&
142          item->bbox()!=m_entries[index]->bbox() )
143     {
144       Q_EMIT updated_bbox(true);
145     }
146 
147     if(emit_item_about_to_be_destroyed) {
148       Q_EMIT itemAboutToBeDestroyed(item);
149       item->aboutToBeDestroyed();
150     }
151 
152     Q_EMIT updated();
153     group =
154             qobject_cast<CGAL::Three::Scene_group_item*>(m_entries[index]);
155     if(group)
156     {
157         addGroup(group);
158         m_groups.append(index);
159     }
160     itemChanged(index);
161     Q_EMIT restoreCollapsedState();
162     redraw_model();
163     Q_EMIT selectionChanged(index);
164     Q_FOREACH(Scene_item* child, group_children)
165     {
166       erase(item_id(child));
167     }
168     return item;
169 }
170 
171 Scene::Item_id
erase(Scene::Item_id index)172 Scene::erase(Scene::Item_id index)
173 {
174   CGAL::Three::Scene_item* item = m_entries[index];
175   if(qobject_cast<Scene_group_item*>(item))
176   {
177     setSelectedItemsList(QList<Scene_interface::Item_id>()<<item_id(item));
178     return erase(selectionIndices());
179   }
180   m_groups.removeAll(index);
181   if(item->parentGroup()
182      && item->parentGroup()->isChildLocked(item))
183     return -1;
184   //clears the Scene_view
185   clear();
186   index_map.clear();
187   if(index < 0 || index >= m_entries.size())
188     return -1;
189   if(item->parentGroup())
190     item->parentGroup()->removeChild(item);
191 
192   //removes the item from all groups that contain it
193   Item_id removed_item = item_id(item);
194   children.removeAll(removed_item);
195   indexErased(removed_item);
196     m_entries.removeAll(item);
197   Q_EMIT itemAboutToBeDestroyed(item);
198   item->aboutToBeDestroyed();
199   item->deleteLater();
200   selected_item = -1;
201   //re-creates the Scene_view
202   Q_FOREACH(Item_id id, children)
203   {
204     organize_items(this->item(id), invisibleRootItem(), 0);
205   }
206   QStandardItemModel::beginResetModel();
207   Q_EMIT updated();
208   QStandardItemModel::endResetModel();
209   Q_EMIT restoreCollapsedState();
210   if(--index >= 0)
211     return index;
212   if(!m_entries.isEmpty())
213     return 0;
214   return -1;
215 }
216 
217 int
erase(QList<int> indices)218 Scene::erase(QList<int> indices)
219 {
220   if(indices.empty())
221     return -1;
222   QList<CGAL::Three::Scene_item*> to_be_removed;
223   int max_index = -1;
224   Q_FOREACH(int index, indices) {
225     if(index < 0 || index >= m_entries.size())
226       continue;
227 
228     max_index = (std::max)(max_index, index);
229     CGAL::Three::Scene_item* item = m_entries[index];
230     if(item->parentGroup()
231        && item->parentGroup()->isChildLocked(item))
232       if(!indices.contains(item_id(item->parentGroup())))
233         continue;
234     Scene_group_item* group = qobject_cast<Scene_group_item*>(item);
235     if(group)
236     {
237       Q_FOREACH(Item_id id, group->getChildren())
238       {
239         CGAL::Three::Scene_item* child = group->getChild(id);
240         if(!to_be_removed.contains(child))
241           to_be_removed.push_back(child);
242       }
243     }
244     if(!to_be_removed.contains(item))
245       to_be_removed.push_back(item);
246   }
247 
248   Q_FOREACH(Scene_item* item, to_be_removed) {
249     Item_id removed_item = item_id(item);
250     if(removed_item == -1) //case of the selection_item, for example.
251       continue;
252     if(item->parentGroup())
253       item->parentGroup()->removeChild(item);
254     children.removeAll(removed_item);
255     indexErased(removed_item);
256     m_groups.removeAll(removed_item);
257     m_entries.removeAll(item);
258 
259     Q_EMIT itemAboutToBeDestroyed(item);
260     item->aboutToBeDestroyed();
261     item->deleteLater();
262   }
263   clear();
264   index_map.clear();
265   selected_item = -1;
266   Q_FOREACH(Item_id id, children)
267   {
268     organize_items(item(id), invisibleRootItem(), 0);
269   }
270   QStandardItemModel::beginResetModel();
271   Q_EMIT updated();
272   QStandardItemModel::endResetModel();
273   Q_EMIT restoreCollapsedState();
274 
275   int index = max_index + 1 - indices.size();
276   if(index >= m_entries.size()) {
277     index = m_entries.size() - 1;
278   }
279   if(index >= 0)
280     return index;
281   if(!m_entries.isEmpty())
282     return 0;
283   return -1;
284 
285 }
286 
remove_item_from_groups(Scene_item * item)287 void Scene::remove_item_from_groups(Scene_item* item)
288 {
289     CGAL::Three::Scene_group_item* group = item->parentGroup();
290     if(group)
291     {
292         group->removeChild(item);
293         children.push_back(item_id(item));
294     }
295 }
~Scene()296 Scene::~Scene()
297 {
298   Q_FOREACH(CGAL::QGLViewer* viewer, CGAL::QGLViewer::QGLViewerPool())
299   {
300     removeViewer(static_cast<CGAL::Three::Viewer_interface*>(viewer));
301     viewer->setProperty("is_destroyed", true);
302   }
303   Q_FOREACH(QOpenGLVertexArrayObject* vao, vaos.values())
304   {
305     vao->destroy();
306     delete vao;
307   }
308   Q_FOREACH(CGAL::Three::Scene_item* item_ptr, m_entries)
309   {
310     item_ptr->deleteLater();
311   }
312   m_entries.clear();
313 }
314 
315 CGAL::Three::Scene_item*
item(Item_id index) const316 Scene::item(Item_id index) const
317 {
318     return m_entries.value(index); // QList::value checks bounds
319 }
320 
321 Scene::Item_id
item_id(CGAL::Three::Scene_item * scene_item) const322 Scene::item_id(CGAL::Three::Scene_item* scene_item) const
323 {
324     return m_entries.indexOf(scene_item);
325 }
326 
327 int
numberOfEntries() const328 Scene::numberOfEntries() const
329 {
330     return m_entries.size();
331 }
332 
333 // Duplicate a scene item.
334 // Return the ID of the new item (-1 on error).
335 Scene::Item_id
duplicate(Item_id index)336 Scene::duplicate(Item_id index)
337 {
338     if(index < 0 || index >= m_entries.size())
339         return -1;
340 
341     const CGAL::Three::Scene_item* item = m_entries[index];
342     CGAL::Three::Scene_item* new_item = item->clone();
343     if(new_item) {
344         new_item->setName(tr("%1 (copy)").arg(item->name()));
345         new_item->setColor(item->color());
346         new_item->setVisible(item->visible());
347         addItem(new_item);
348         return m_entries.size() - 1;
349     }
350     else
351         return -1;
352 }
353 
initializeGL(CGAL::Three::Viewer_interface * viewer)354 void Scene::initializeGL(CGAL::Three::Viewer_interface* viewer)
355 {
356 
357   //Vertex source code
358   const char vertex_source[] =
359   {
360     "#version 150                                 \n"
361     "in vec4 vertex;                \n"
362     "in vec2 v_texCoord;            \n"
363     "uniform mat4 projection_matrix;       \n"
364     "out vec2 f_texCoord;              \n"
365     "void main(void)                             \n"
366     "{                                           \n"
367     "  f_texCoord = v_texCoord;                  \n"
368     "  gl_Position = projection_matrix * vertex; \n"
369     "}                                           \n"
370 
371   };
372 
373   const char vertex_source_comp[] =
374   {
375     "attribute highp vec4 vertex;                \n"
376     "attribute highp vec2 v_texCoord;            \n"
377     "uniform highp mat4 projection_matrix;       \n"
378     "varying highp vec2 f_texCoord;              \n"
379     "void main(void)                             \n"
380     "{                                           \n"
381     "  f_texCoord = v_texCoord;                  \n"
382     "  gl_Position = projection_matrix * vertex; \n"
383     "}                                           \n"
384 
385   };
386   //Fragment source code
387   const char fragment_source[] =
388   {
389     "#version 150                                                            \n"
390     "in vec2 f_texCoord;                                         \n"
391     "out vec4 out_color ; \n"
392     "uniform sampler2D s_texture;                                             \n"
393     "void main(void)                                                        \n"
394     "{                                                                      \n"
395     "  out_color = texture(s_texture, f_texCoord); \n"
396     "}                                                                      \n"
397   };
398   const char fragment_source_comp[] =
399   {
400     "varying highp vec2 f_texCoord;                                         \n"
401     "uniform sampler2D texture;                                             \n"
402     "void main(void)                                                        \n"
403     "{                                                                      \n"
404     "  gl_FragColor = texture2D(texture, f_texCoord); \n"
405     "}                                                                      \n"
406   };
407 
408 
409   QOpenGLShader vertex_shader(QOpenGLShader::Vertex);
410   QOpenGLShader fragment_shader(QOpenGLShader::Fragment);
411   if(viewer->isOpenGL_4_3())
412   {
413     if(!vertex_shader.compileSourceCode(vertex_source))
414     {
415       std::cerr<<"Compiling vertex source FAILED"<<std::endl;
416     }
417 
418     if(!fragment_shader.compileSourceCode(fragment_source))
419     {
420       std::cerr<<"Compiling fragmentsource FAILED"<<std::endl;
421     }
422   }
423   else
424   {
425     if(!vertex_shader.compileSourceCode(vertex_source_comp))
426     {
427       std::cerr<<"Compiling vertex source FAILED"<<std::endl;
428     }
429 
430     if(!fragment_shader.compileSourceCode(fragment_source_comp))
431     {
432       std::cerr<<"Compiling fragmentsource FAILED"<<std::endl;
433     }
434   }
435 
436   if(!program.addShader(&vertex_shader))
437   {
438     std::cerr<<"adding vertex shader FAILED"<<std::endl;
439   }
440   if(!program.addShader(&fragment_shader))
441   {
442     std::cerr<<"adding fragment shader FAILED"<<std::endl;
443   }
444   if(!program.link())
445   {
446     //std::cerr<<"linking Program FAILED"<<std::endl;
447     qDebug() << program.log();
448   }
449   points[0] = -1.0f; points[1] = -1.0f; points[2] = 0.0f;
450   points[3] = 1.0f; points[4] = 1.0f; points[5] = 0.0f;
451   points[6] = 1.0f; points[7] = -1.0f; points[8] = 0.0f;
452   points[9] = -1.0f; points[10] = -1.0f; points[11] = 0.0f;
453   points[12] = -1.0f; points[13] = 1.0f; points[14] = 0.0f;
454   points[15] = 1.0f; points[16] = 1.0f; points[17] = 0.0f;
455 
456   uvs[0] = 0.0f; uvs[1] = 0.0f;
457   uvs[2] = 1.0f; uvs[3] = 1.0f;
458   uvs[4] = 1.0f; uvs[5] = 0.0f;
459   uvs[6] = 0.0f; uvs[7] = 0.0f;
460   uvs[8] = 0.0f; uvs[9] = 1.0f;
461   uvs[10] = 1.0f; uvs[11] = 1.0f;
462 
463   vbo[0].create();
464   vbo[1].create();
465 
466   viewer->makeCurrent();
467   vaos[viewer] = new QOpenGLVertexArrayObject();
468   vaos[viewer]->create();
469   program.bind();
470   vaos[viewer]->bind();
471   vbo[0].bind();
472   vbo[0].allocate(points, 18 * sizeof(float));
473   program.enableAttributeArray("vertex");
474   program.setAttributeArray("vertex", GL_FLOAT, nullptr, 3);
475   vbo[0].release();
476 
477   vbo[1].bind();
478   vbo[1].allocate(uvs, 12 * sizeof(float));
479   program.enableAttributeArray("v_texCoord");
480   program.setAttributeArray("v_texCoord", GL_FLOAT, nullptr, 2);
481   vbo[1].release();
482   vaos[viewer]->release();
483   program.release();
484   gl_init = true;
485 }
486 
s_itemAboutToBeDestroyed(CGAL::Three::Scene_item * rmv_itm)487 void Scene::s_itemAboutToBeDestroyed(CGAL::Three::Scene_item *rmv_itm)
488 {
489  Q_FOREACH(CGAL::Three::Scene_item* item, m_entries)
490  {
491    if(item == rmv_itm)
492      item->itemAboutToBeDestroyed(item);
493  }
494 }
495 bool
keyPressEvent(QKeyEvent * e)496 Scene::keyPressEvent(QKeyEvent* e){
497     bool res=false;
498     for (QList<int>::iterator it=selected_items_list.begin(),endit=selected_items_list.end();
499          it!=endit;++it)
500     {
501         CGAL::Three::Scene_item* item=m_entries[*it];
502         res |= item->keyPressEvent(e);
503     }
504     return res;
505 }
506 
507 void
draw(CGAL::Three::Viewer_interface * viewer)508 Scene::draw(CGAL::Three::Viewer_interface* viewer)
509 {
510     draw_aux(false, viewer);
511 }
512 void
drawWithNames(CGAL::Three::Viewer_interface * viewer)513 Scene::drawWithNames(CGAL::Three::Viewer_interface* viewer)
514 {
515     draw_aux(true, viewer);
516 }
517 
item_should_be_skipped_in_draw(Scene_item * item)518 bool item_should_be_skipped_in_draw(Scene_item* item) {
519   if(!item->visible()) return true;
520   if(item->has_group == 0) return false;
521   Scene_group_item* group = item->parentGroup();
522   while(group != nullptr) {
523     if(!group->visible()) return false;
524     group = group->parentGroup();
525   }
526   return true;
527 }
528 
529 
renderScene(const QList<Scene_interface::Item_id> & items,Viewer_interface * viewer,QMap<float,int> & picked_item_IDs,bool with_names,int pass,bool writing_depth,QOpenGLFramebufferObject * fbo)530 void Scene::renderScene(const QList<Scene_interface::Item_id> &items,
531                         Viewer_interface *viewer,
532                         QMap<float, int>& picked_item_IDs,
533                         bool with_names,
534                         int pass,
535                         bool writing_depth,
536                         QOpenGLFramebufferObject *fbo)
537 {
538   viewer->setCurrentPass(pass);
539   viewer->setDepthWriting(writing_depth);
540   viewer->setDepthPeelingFbo(fbo);
541   Q_FOREACH(Scene_interface::Item_id index, items)
542   {
543     CGAL::Three::Scene_item& item = *m_entries[index];
544     CGAL::Three::Scene_group_item* group =
545         qobject_cast<CGAL::Three::Scene_group_item*>(&item);
546     if(index == selected_item || selected_items_list.contains(index))
547     {
548       item.selection_changed(true);
549     }
550     else
551     {
552       item.selection_changed(false);
553     }
554     if(group ||item.visible())
555     {
556       if( group || item.renderingMode() == Flat || item.renderingMode() == FlatPlusEdges || item.renderingMode() == Gouraud || item.renderingMode() == GouraudPlusEdges )
557       {
558         if(with_names) {
559           viewer->glClearDepthf(1.0);
560           viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
561         }
562         item.draw(viewer);
563         if(with_names) {
564 
565           //    read depth buffer at pick location;
566           float depth = 1.0;
567           viewer->glReadPixels(picked_pixel.x(),viewer->camera()->screenHeight()-1-picked_pixel.y(),1,1,GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
568           if (depth != 1.0)
569           {
570             //add object to list of picked objects;
571             picked_item_IDs[depth] = index;
572           }
573         }
574       }
575       if(group)
576         group->renderChildren(viewer, picked_item_IDs, picked_pixel, with_names);
577     }
578   }
579 }
580 
renderWireScene(const QList<Scene_interface::Item_id> & items,Viewer_interface * viewer,QMap<float,int> & picked_item_IDs,bool with_names)581 void Scene::renderWireScene(const QList<Scene_interface::Item_id> &items,
582                             Viewer_interface *viewer,
583                             QMap<float, int>& picked_item_IDs,
584                             bool with_names)
585 {
586   Q_FOREACH(Scene_interface::Item_id index, items)
587    {
588      CGAL::Three::Scene_item& item = *m_entries[index];
589      CGAL::Three::Scene_group_item* group =
590          qobject_cast<CGAL::Three::Scene_group_item*>(&item);
591      if(index == selected_item || selected_items_list.contains(index))
592      {
593          item.selection_changed(true);
594      }
595      else
596      {
597          item.selection_changed(false);
598      }
599 
600      if(group ||item.visible())
601      {
602        if( group || (!with_names && item.renderingMode() == FlatPlusEdges )
603           || item.renderingMode() == Wireframe
604           || item.renderingMode() == PointsPlusNormals
605           || item.renderingMode() == GouraudPlusEdges)
606        {
607          if(with_names) {
608            viewer->glClearDepthf(1.0);
609            viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
610          }
611          viewer->setGlPointSize(2.f);
612          item.drawEdges(viewer);
613        }
614        else{
615            if( item.renderingMode() == PointsPlusNormals ){
616                viewer->setGlPointSize(2.f);
617                if(index == selected_item || selected_items_list.contains(index))
618                {
619 
620                  item.selection_changed(true);
621                }
622                else
623                {
624 
625                  item.selection_changed(false);
626                }
627                item.drawEdges(viewer);
628            }
629        }
630 
631        if((item.renderingMode() == Wireframe || item.renderingMode() == PointsPlusNormals )
632           && with_names)
633        {
634 
635          //    read depth buffer at pick location;
636          float depth = 1.0;
637          viewer->glReadPixels(picked_pixel.x(),viewer->camera()->screenHeight()-1-picked_pixel.y(),1,1,GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
638          if (depth != 1.0)
639          {
640            //add object to list of picked objects;
641            picked_item_IDs[depth] = index;
642          }
643        }
644      }
645    }
646 }
647 
renderPointScene(const QList<Scene_interface::Item_id> & items,Viewer_interface * viewer,QMap<float,int> & picked_item_IDs,bool with_names)648 void Scene::renderPointScene(const QList<Scene_interface::Item_id> &items,
649                              Viewer_interface *viewer,
650                              QMap<float, int>& picked_item_IDs,
651                              bool with_names)
652 {
653   Q_FOREACH(Scene_interface::Item_id index, items)
654   {
655     CGAL::Three::Scene_item& item = *m_entries[index];
656     CGAL::Three::Scene_group_item* group =
657         qobject_cast<CGAL::Three::Scene_group_item*>(&item);
658     if(group ||item.visible())
659     {
660       if(item.renderingMode() == Points && with_names) {
661           viewer->glClearDepthf(1.0);
662           viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
663       }
664 
665       if(group || item.renderingMode() == Points  ||
666          (item.renderingMode() == PointsPlusNormals)  ||
667          (item.renderingMode() == ShadedPoints))
668       {
669         if(with_names) {
670           viewer->glClearDepthf(1.0);
671           viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
672         }
673         viewer->setGlPointSize(3.0f);
674         item.drawPoints(viewer);
675       }
676       if(item.renderingMode() == Points && with_names) {
677         //    read depth buffer at pick location;
678         float depth = 1.0;
679         viewer->glReadPixels(picked_pixel.x(),viewer->camera()->screenHeight()-1-picked_pixel.y(),1,1,GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
680         if (depth != 1.0)
681         {
682           //add object to list of picked objects;
683           picked_item_IDs[depth] = index;
684         }
685       }
686     }
687   }
688 }
689 
690 
has_alpha()691  bool Scene::has_alpha()
692  {
693    Q_FOREACH(Scene_item* item, m_entries)
694      if(item->alpha() != 1.0f)
695        return true;
696    return false;
697  }
698 void
draw_aux(bool with_names,CGAL::Three::Viewer_interface * viewer)699 Scene::draw_aux(bool with_names, CGAL::Three::Viewer_interface* viewer)
700 {
701     QMap<float, int> picked_item_IDs;
702     if(with_names)
703       viewer->glEnable(GL_DEPTH_TEST);
704     if(!gl_init)
705         initializeGL(viewer);
706     //treat opaque items first to ensure that when two items are the same, but only one is opaque,
707     //the item stays opaque
708     QList<Item_id> opaque_items;
709     QList<Item_id> transparent_items;
710     Q_FOREACH(Item_id id, children)
711     {
712       Scene_item* item = m_entries[id];
713       Scene_group_item* group = qobject_cast<Scene_group_item*>(item);
714       bool is_transparent=false;
715       if(item->alpha() != 1.0f)
716         is_transparent = true;
717       else if(group)
718       {
719         for(const auto& child : group->getChildren())
720         {
721           if(group->getChild(child)->alpha() < 1.0f)
722           {
723             is_transparent = true;
724             break;
725           }
726         }
727       }
728       if(!is_transparent)
729         opaque_items.push_back(id);
730       else
731         transparent_items.push_back(id);
732     }
733     renderScene(children, viewer, picked_item_IDs, with_names, -1, false, nullptr);
734     if(with_names)
735     {
736       //here we get the selected point, before erasing the depth buffer. We store it
737       //in a dynamic property as a QList<double>. If there is some alpha, the
738       //depth buffer is altered, and the picking will return true even when it is
739       // performed in the background, when it should return false. To avoid that,
740       // we distinguish the case were there is no alpha, to let the viewer
741       //perform it, and the case where the pixel is not found. In the first case,
742       //we erase the property, in the latter we return an empty list.
743       //According ot that, in the viewer, either we perform the picking, either we do nothing.
744       if(has_alpha()) {
745         bool found = false;
746         CGAL::qglviewer::Vec point = viewer->camera()->pointUnderPixel(picked_pixel, found) - viewer->offset();
747         if(found){
748           QList<QVariant> picked_point;
749           picked_point <<point.x
750                       <<point.y
751                      <<point.z;
752           viewer->setProperty("picked_point", picked_point);
753         }
754         else{
755           viewer->setProperty("picked_point", QList<QVariant>());
756         }
757       }
758       else {
759         viewer->setProperty("picked_point", {});
760       }
761     }
762     if(!with_names && has_alpha())
763     {
764       std::vector<QOpenGLFramebufferObject*> fbos;
765       std::vector<QOpenGLFramebufferObject*> depth_test;
766       QColor background = viewer->backgroundColor();
767       fbos.resize((int)viewer->total_pass());
768       depth_test.resize((int)viewer->total_pass()-1);
769 
770       //first pass
771       fbos[0] = new QOpenGLFramebufferObject(viewer->width(), viewer->height(),QOpenGLFramebufferObject::Depth, GL_TEXTURE_2D, GL_RGBA32F);
772       fbos[0]->bind();
773       viewer->glDisable(GL_BLEND);
774       viewer->glEnable(GL_DEPTH_TEST);
775       viewer->glDepthFunc(GL_LESS);
776       viewer->glClearColor(0.0f,
777                            0.0f,
778                            0.0f,
779                            0.0f);
780       viewer->glClearDepthf(1);
781       viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
782       renderScene(opaque_items, viewer, picked_item_IDs, false, 0,false, nullptr);
783       renderScene(transparent_items, viewer, picked_item_IDs, false, 0,false, nullptr);
784       fbos[0]->release();
785       depth_test[0] = new QOpenGLFramebufferObject(viewer->width(), viewer->height(),QOpenGLFramebufferObject::Depth, GL_TEXTURE_2D, GL_RGBA32F);
786       depth_test[0]->bind();
787       viewer->glDisable(GL_BLEND);
788       viewer->glEnable(GL_DEPTH_TEST);
789       viewer->glDepthFunc(GL_LESS);
790       viewer->glClearColor(0.0f,
791                            0.0f,
792                            0.0f,
793                            0.0f);
794       viewer->glClearDepthf(1);
795       viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
796       renderScene(opaque_items, viewer, picked_item_IDs, false, 0,true, nullptr);
797       renderScene(transparent_items, viewer, picked_item_IDs, false, 0,true, nullptr);
798       depth_test[0]->release();
799 
800       //other passes
801       for(int i=1; i<viewer->total_pass()-1; ++i)
802       {
803         fbos[i] = new QOpenGLFramebufferObject(viewer->width(), viewer->height(),QOpenGLFramebufferObject::Depth, GL_TEXTURE_2D, GL_RGBA32F);
804         fbos[i]->bind();
805         viewer->glDisable(GL_BLEND);
806         viewer->glEnable(GL_DEPTH_TEST);
807         viewer->glDepthFunc(GL_LESS);
808         viewer->glClearColor(0.0f,
809                              0.0f,
810                              0.0f,
811                              0.0f);
812         viewer->glClearDepthf(1);
813         viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
814         renderWireScene(children, viewer, picked_item_IDs, false);
815         renderPointScene(children, viewer, picked_item_IDs, false);
816         renderScene(opaque_items     , viewer, picked_item_IDs, false, i, false, depth_test[i-1]);
817         renderScene(transparent_items, viewer, picked_item_IDs, false, i, false, depth_test[i-1]);
818         fbos[i]->release();
819 
820         depth_test[i] = new QOpenGLFramebufferObject(viewer->width(), viewer->height(),QOpenGLFramebufferObject::Depth, GL_TEXTURE_2D, GL_RGBA32F);
821         depth_test[i]->bind();
822         viewer->glDisable(GL_BLEND);
823         viewer->glEnable(GL_DEPTH_TEST);
824         viewer->glDepthFunc(GL_LESS);
825         viewer->glClearColor(0.0f,
826                              0.0f,
827                              0.0f,
828                              0.0f);
829         viewer->glClearDepthf(1);
830         viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
831         renderScene(opaque_items     , viewer, picked_item_IDs, false, i, true, depth_test[i-1]);
832         renderScene(transparent_items, viewer, picked_item_IDs, false, i, true, depth_test[i-1]);
833         depth_test[i]->release();
834       }
835 
836 
837       //last pass
838       fbos[(int)viewer->total_pass()-1] = new QOpenGLFramebufferObject(viewer->width(), viewer->height(),QOpenGLFramebufferObject::Depth, GL_TEXTURE_2D, GL_RGBA32F);
839       fbos[(int)viewer->total_pass()-1]->bind();
840       viewer->glDisable(GL_BLEND);
841       viewer->glEnable(GL_DEPTH_TEST);
842       viewer->glDepthFunc(GL_LESS);
843       viewer->glClearColor(0.0f,
844                            0.0f,
845                            0.0f,
846                            0.0f);
847       viewer->glClearDepthf(1);
848       viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
849       renderScene(opaque_items     , viewer, picked_item_IDs, false, (int)viewer->total_pass()-1, false, depth_test[(int)viewer->total_pass()-2]);
850       renderScene(transparent_items, viewer, picked_item_IDs, false, (int)viewer->total_pass()-1, false, depth_test[(int)viewer->total_pass()-2]);
851       fbos[(int)viewer->total_pass()-1]->release();
852       if(viewer->getStoredFrameBuffer() != nullptr)
853         viewer->getStoredFrameBuffer()->bind();
854 
855       //blending
856       program.bind();
857       vaos[viewer]->bind();
858       viewer->glClearColor((GLclampf)background.redF(),
859                            (GLclampf)background.greenF(),
860                            (GLclampf)background.blueF(),
861                            0.0f);
862       viewer->glDisable(GL_DEPTH_TEST);
863       viewer->glClear(GL_COLOR_BUFFER_BIT);
864       viewer->glEnable(GL_BLEND);
865       viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
866 
867       QMatrix4x4 proj_mat;
868       proj_mat.setToIdentity();
869       proj_mat.ortho(-1,1,-1,1,0,1);
870       program.setUniformValue("projection_matrix", proj_mat);
871       for(int i=0; i< (int)viewer->total_pass()-1; ++i)
872         delete depth_test[i];
873       for(int i = (int)viewer->total_pass()-1; i>=0; --i)
874       {
875         viewer->glBindTexture(GL_TEXTURE_2D, fbos[i]->texture());
876         viewer->glDrawArrays(GL_TRIANGLES,0,static_cast<GLsizei>(6));
877         delete fbos[i];
878       }
879       viewer->glDisable(GL_BLEND);
880       viewer->glEnable(GL_DEPTH_TEST);
881       vaos[viewer]->release();
882       program.release();
883     }
884 
885     viewer->glDepthFunc(GL_LEQUAL);
886     // Wireframe OpenGL drawing
887     renderWireScene(children, viewer, picked_item_IDs, with_names);
888     // Points OpenGL drawing
889     renderPointScene(children, viewer, picked_item_IDs, with_names);
890 
891     if(with_names)
892     {
893         QList<float> depths = picked_item_IDs.keys();
894         if(!depths.isEmpty())
895         {
896             std::sort(depths.begin(), depths.end());
897             int id = picked_item_IDs[depths.first()];
898             setSelectedItemIndex(id);
899             viewer->setSelectedName(id);
900 
901         }
902     }
903     if(with_names)
904         picked = true;
905     else
906         picked = false;
907     //scrolls the sceneView to the selected item's line.
908     if(picked)
909     {
910         Q_EMIT(itemPicked(index_map.key(mainSelectionIndex())));
911     }
912     Q_EMIT drawFinished();
913 }
914 
915 // workaround for Qt-4.2 (see above)
916 #undef lighter
917 QVariant
data(const QModelIndex & index,int role) const918 Scene::data(const QModelIndex &index, int role) const
919 {
920     if (!index.isValid())
921     {
922         return QVariant();
923     }
924 
925     int id = index_map[index];
926     if(id < 0 || id >= m_entries.size())
927         return QVariant();
928     if(role == ::Qt::ToolTipRole)
929     {
930         return m_entries[id]->toolTip();
931     }
932     switch(index.column())
933     {
934     case ColorColumn:
935         if(role == ::Qt::DecorationRole)
936             return m_entries.value(id)->color();
937         break;
938     case NameColumn:
939         if(role == ::Qt::DisplayRole || role == ::Qt::EditRole)
940             return m_entries.value(id)->name();
941         if(role == ::Qt::FontRole)
942             return m_entries.value(id)->font();
943         break;
944     case RenderingModeColumn:
945         if(role == ::Qt::DisplayRole) {
946             return m_entries.value(id)->renderingModeName();
947         }
948         else if(role == ::Qt::EditRole) {
949             return static_cast<int>(m_entries.value(id)->renderingMode());
950         }
951         else if(role == ::Qt::TextAlignmentRole) {
952             return ::Qt::AlignCenter;
953         }
954         break;
955     case ABColumn:
956         if(role == ::Qt::DisplayRole) {
957             if(id == item_A)
958                 return "A";
959             if(id == item_B)
960                 return "B";
961         }
962         else if(role == ::Qt::TextAlignmentRole) {
963             return ::Qt::AlignLeft;
964         }
965         break;
966     case VisibleColumn:
967         if(role == ::Qt::DisplayRole || role == ::Qt::EditRole)
968             return m_entries.value(id)->visible();
969         break;
970     default:
971         return QVariant();
972     }
973     return QVariant();
974 }
975 
976 QVariant
headerData(int section,::Qt::Orientation orientation,int role) const977 Scene::headerData ( int section, ::Qt::Orientation orientation, int role ) const
978 {
979     if(orientation == ::Qt::Horizontal)  {
980         if (role == ::Qt::DisplayRole)
981         {
982             switch(section)
983             {
984             case NameColumn:
985                 return tr("Name");
986                 break;
987             case ColorColumn:
988                 return tr("#");
989                 break;
990             case RenderingModeColumn:
991                 return tr("Mode");
992             case ABColumn:
993                 return tr("A/B");
994                 break;
995             case VisibleColumn:
996                 return tr("View");
997                 break;
998             default:
999                 return QVariant();
1000             }
1001         }
1002         else if(role == ::Qt::ToolTipRole) {
1003             if(section == RenderingModeColumn) {
1004                 return tr("Rendering mode (points/wireframe/flat/flat+edges/Gouraud)");
1005             }
1006             else if(section == ABColumn) {
1007                 return tr("Selection A/Selection B");
1008             }
1009         }
1010     }
1011     return QStandardItemModel::headerData(section, orientation, role);
1012 }
1013 
1014 Qt::ItemFlags
flags(const QModelIndex & index) const1015 Scene::flags ( const QModelIndex & index ) const
1016 {
1017     if (index.isValid() && index.column() == NameColumn) {
1018         return QStandardItemModel::flags(index) | ::Qt::ItemIsEditable;
1019     }
1020     else {
1021         return QStandardItemModel::flags(index);
1022     }
1023 }
1024 
1025 bool
setData(const QModelIndex & index,const QVariant & value,int role)1026 Scene::setData(const QModelIndex &index,
1027                const QVariant &value,
1028                int role)
1029 {
1030 
1031     if( role != ::Qt::EditRole || !index.isValid() )
1032         return false;
1033 
1034     int id = index_map[index];
1035     if(id < 0 || id >= m_entries.size()){
1036         return false;
1037     }
1038 
1039     CGAL::Three::Scene_item* item = m_entries[id];
1040 
1041     if(!item) return false;
1042     switch(index.column())
1043     {
1044     case NameColumn:
1045         item->setName(value.toString());
1046     Q_EMIT dataChanged(index, index);
1047         return true;
1048         break;
1049     case ColorColumn:
1050       if(selectionIndices().contains(item_id(item)))
1051         Q_FOREACH(Item_id item_index, selectionIndices())
1052           this->item(item_index)->setColor(value.value<QColor>());
1053       else
1054         item->setColor(value.value<QColor>());
1055     Q_EMIT dataChanged(index, index);
1056         return true;
1057         break;
1058     case RenderingModeColumn:
1059     {
1060         RenderingMode rendering_mode = static_cast<RenderingMode>(value.toInt());
1061         // Find next supported rendering mode
1062         int counter = 0;
1063         while ( ! item->supportsRenderingMode(rendering_mode)
1064                 )
1065         {
1066             rendering_mode = static_cast<RenderingMode>( (rendering_mode+1) % NumberOfRenderingMode );
1067             if(counter++ == NumberOfRenderingMode)
1068               break;
1069         }
1070         item->setRenderingMode(rendering_mode);
1071         QModelIndex nindex = createIndex(m_entries.size()-1,RenderingModeColumn+1);
1072     Q_EMIT dataChanged(index, nindex);
1073         return true;
1074         break;
1075     }
1076     case VisibleColumn:
1077         item->setVisible(value.toBool());
1078     Q_EMIT dataChanged(index, createIndex(m_entries.size()-1,VisibleColumn+1));
1079         return true;
1080     default:
1081         return false;
1082     }
1083     return false;
1084 }
1085 
dropMimeData(const QMimeData *,Qt::DropAction,int,int,const QModelIndex & parent)1086 bool Scene::dropMimeData(const QMimeData * /*data*/,
1087                          Qt::DropAction /*action*/,
1088                          int /*row*/,
1089                          int /*column*/,
1090                          const QModelIndex &parent)
1091 {
1092     //gets the moving items
1093     QList<Scene_item*> items;
1094     QList<int> groups_children;
1095 
1096     //get IDs of all children of selected groups
1097     Q_FOREACH(int i, selected_items_list)
1098     {
1099       CGAL::Three::Scene_group_item* group =
1100           qobject_cast<CGAL::Three::Scene_group_item*>(item(i));
1101       if(group)
1102       {
1103         Q_FOREACH(Item_id id, group->getChildren())
1104         {
1105           CGAL::Three::Scene_item* child = item(id);
1106           groups_children << item_id(child);
1107         }
1108       }
1109     }
1110     // Insure that children of selected groups will not be added twice
1111     Q_FOREACH(int i, selected_items_list)
1112     {
1113       if(!groups_children.contains(i))
1114       {
1115         items << item(i);
1116       }
1117     }
1118     //Gets the group at the drop position
1119     CGAL::Three::Scene_group_item* group = nullptr;
1120     if(parent.isValid())
1121         group = qobject_cast<CGAL::Three::Scene_group_item*>(this->item(index_map[parent]));
1122     bool one_contained = false;
1123     if(group)
1124     {
1125       Q_FOREACH(int id, selected_items_list)
1126         if(group->getChildren().contains(id))
1127         {
1128           one_contained = true;
1129           break;
1130 
1131         }
1132     }
1133     //if the drop item is not a group_item or if it already contains the item, then the drop action must be ignored
1134     if(!group ||one_contained)
1135     {
1136       //unless the drop zone is empty, which means the item should be removed from all groups.
1137       if(!parent.isValid())
1138       {
1139         Q_FOREACH(Scene_item* item, items)
1140         {
1141           if(item->parentGroup())
1142           {
1143             item->parentGroup()->removeChild(item);
1144             addChild(item);
1145           }
1146         }
1147         redraw_model();
1148         return true;
1149       }
1150       return false;
1151     }
1152     Q_FOREACH(Scene_item* item, items)
1153       changeGroup(item, group);
1154     redraw_model();
1155     return true;
1156 }
1157 
1158 //todo : if a group is selected, don't treat it's children.
sort_lists(QVector<QList<int>> & sorted_lists,bool up)1159 bool Scene::sort_lists(QVector<QList<int> >&sorted_lists, bool up)
1160 {
1161   QVector<int> group_found;
1162   Q_FOREACH(int i, selectionIndices())
1163   {
1164     Scene_item* item = this->item(i);
1165     if(item->has_group == 0)
1166     {
1167       sorted_lists.first().push_back(i);
1168     }
1169     else
1170     {
1171       int group_id = item_id(item->parentGroup());
1172       if(group_found.contains(group_id))
1173         sorted_lists[group_id].push_back(i);
1174       else
1175       {
1176         group_found.push_back(group_id);
1177         if(sorted_lists.size() < group_id+1)
1178           sorted_lists.resize(group_id+1);
1179         sorted_lists[group_id].push_back(i);
1180       }
1181     }
1182   }
1183   //iterate the first list to find the groups that are selected and remove the corresponding
1184   //sub lists.
1185   //todo: do that for each group. (treat subgroups)
1186   for(int i = 0; i< sorted_lists.first().size(); ++i)
1187   {
1188     Scene_group_item* group = qobject_cast<Scene_group_item*>(this->item(sorted_lists.first()[i]));
1189     if(group && ! group->getChildren().isEmpty() && sorted_lists.first()[i] < sorted_lists.size())
1190     {
1191       sorted_lists[sorted_lists.first()[i]].clear();
1192     }
1193   }
1194   std::sort(sorted_lists.first().begin(), sorted_lists.first().end(),
1195             [this](int a, int b) {
1196     return children.indexOf(a) < children.indexOf(b);
1197 });
1198   if(!sorted_lists.first().isEmpty())
1199   {
1200     if(up &&  children.indexOf(sorted_lists.first().first()) == 0)
1201       return false;
1202     else if(!up &&  children.indexOf(sorted_lists.first().last()) == children.size() -1)
1203       return false;
1204   }
1205   for(int i=1; i<sorted_lists.size(); ++i)
1206   {
1207     QList<int>& list = sorted_lists[i];
1208     if(list.isEmpty())
1209       continue;
1210     Scene_group_item* group = qobject_cast<Scene_group_item*>(this->item(i));
1211     if(!group)
1212       continue;
1213     std::sort(list.begin(), list.end(),
1214               [group](int a, int b) {
1215       return group->getChildren().indexOf(a) < group->getChildren().indexOf(b);
1216   });
1217     if(up && group->getChildren().indexOf(list.first()) == 0)
1218       return false;
1219     else if(!up && group->getChildren().indexOf(list.last()) == group->getChildren().size()-1)
1220       return false;
1221   }
1222   return true;
1223 }
moveRowUp()1224 void Scene::moveRowUp()
1225 {
1226   if(selectionIndices().isEmpty())
1227     return;
1228   QVector<QList<int> >sorted_lists(1);
1229   QList<int> to_select;
1230   //sort lists according to the indices of each item in its container (scene or group)
1231   //if moving one up would put it out of range, then we stop and do nothing.
1232   if(!sort_lists(sorted_lists, true))
1233     return;
1234 
1235   for(int i=0; i<sorted_lists.first().size(); ++i)
1236   {
1237     Item_id selected_id = sorted_lists.first()[i];
1238     Scene_item* selected_item = item(selected_id);
1239     if(!selected_item)
1240       return;
1241     if(index_map.key(selected_id).row() > 0)
1242     {
1243       //if not in group
1244       QModelIndex baseId = index_map.key(selected_id);
1245       int newId = children.indexOf(
1246             index_map.value(index(baseId.row()-1, baseId.column(),baseId.parent()))) ;
1247       children.move(children.indexOf(selected_id), newId);
1248       redraw_model();
1249       to_select.append(m_entries.indexOf(selected_item));
1250     }
1251   }
1252   for(int i=1; i<sorted_lists.size(); ++i)
1253   {
1254     for(int j = 0; j< sorted_lists[i].size(); ++j)
1255     {
1256       Item_id selected_id = sorted_lists[i][j];
1257       Scene_item* selected_item = item(selected_id);
1258       if(!selected_item)
1259         return;
1260       if(index_map.key(selected_id).row() > 0)
1261       {
1262         Scene_group_item* group = selected_item->parentGroup();
1263         if(group)
1264         {
1265           int id = group->getChildren().indexOf(item_id(selected_item));
1266           group->moveUp(id);
1267           redraw_model();
1268           to_select.append(m_entries.indexOf(selected_item));
1269         }
1270       }
1271     }
1272   }
1273   if(!to_select.isEmpty()){
1274     selectionChanged(to_select);
1275   }
1276 }
moveRowDown()1277 void Scene::moveRowDown()
1278 {
1279   if(selectionIndices().isEmpty())
1280     return;
1281   QVector<QList<int> >sorted_lists(1);
1282   QList<int> to_select;
1283   //sort lists according to the indices of each item in its container (scene or group)
1284   //if moving one up would put it out of range, then we stop and do nothing.
1285   if(!sort_lists(sorted_lists, false))
1286     return;
1287   for(int i=sorted_lists.first().size()-1; i>=0; --i)
1288   {
1289     Item_id selected_id = sorted_lists.first()[i];
1290     Scene_item* selected_item = item(selected_id);
1291     if(!selected_item)
1292       return;
1293     if(index_map.key(selected_id).row() < rowCount(index_map.key(selected_id).parent())-1)
1294     {
1295         //if not in group
1296         QModelIndex baseId = index_map.key(selected_id);
1297         int newId = children.indexOf(
1298               index_map.value(index(baseId.row()+1, baseId.column(),baseId.parent()))) ;
1299         children.move(children.indexOf(selected_id), newId);
1300 
1301       redraw_model();
1302       to_select.prepend(m_entries.indexOf(selected_item));
1303     }
1304   }
1305   for(int i=1; i<sorted_lists.size(); ++i){
1306     if(sorted_lists[i].isEmpty())
1307       continue;
1308     for(int j = sorted_lists[i].size()-1; j >=0; --j)
1309     {
1310       Item_id selected_id = sorted_lists[i][j];
1311       Scene_item* selected_item = item(selected_id);
1312       if(!selected_item)
1313         return;
1314       if(index_map.key(selected_id).row() < rowCount(index_map.key(selected_id).parent())-1)
1315       {
1316         if(item(selected_id)->has_group >0)
1317         {
1318           Scene_group_item* group = selected_item->parentGroup();
1319           if(group)
1320           {
1321             int id = group->getChildren().indexOf(item_id(selected_item));
1322             group->moveDown(id);
1323           }
1324         }
1325         redraw_model();
1326         to_select.prepend(m_entries.indexOf(selected_item));
1327       }
1328     }
1329   }
1330   if(!to_select.isEmpty()){
1331     selectionChanged(to_select);
1332   }
1333 }
mainSelectionIndex() const1334 Scene::Item_id Scene::mainSelectionIndex() const {
1335     return (selectionIndices().size() == 1) ? selected_item : -1;
1336 }
1337 
selectionIndices() const1338 QList<int> Scene::selectionIndices() const {
1339     return selected_items_list;
1340 }
1341 
selectionAindex() const1342 int Scene::selectionAindex() const {
1343     return item_A;
1344 }
1345 
selectionBindex() const1346 int Scene::selectionBindex() const {
1347     return item_B;
1348 }
1349 
createSelection(int i)1350 QItemSelection Scene::createSelection(int i)
1351 {
1352     return QItemSelection(index_map.keys(i).at(0),
1353                           index_map.keys(i).at(4));
1354 }
1355 
createSelection(QList<int> is)1356 QItemSelection Scene::createSelection(QList<int> is)
1357 {
1358     QItemSelection sel;
1359     Q_FOREACH(int i, is)
1360       sel.select(index_map.keys(i).at(0),
1361                  index_map.keys(i).at(4));
1362     return sel;
1363 }
1364 
createSelectionAll()1365 QItemSelection Scene::createSelectionAll()
1366 {
1367   //it is not possible to directly create a selection with items that have different parents, so
1368   //we do it iteratively.
1369   QItemSelection sel;
1370   sel.select(this->createIndex(0, 0),
1371              this->createIndex(m_entries.size(), LastColumn));
1372   for(const auto& gid : m_groups)
1373   {
1374     CGAL::Three::Scene_group_item* group =
1375         qobject_cast<CGAL::Three::Scene_group_item*>(item(gid));
1376     sel.select(index_map.keys(group->getChildren().first()).at(0),
1377                index_map.keys(group->getChildren().last()).at(4));
1378   }
1379   return sel;
1380 }
1381 
itemChanged()1382 void Scene::itemChanged()
1383 {
1384     CGAL::Three::Scene_item* item = qobject_cast<CGAL::Three::Scene_item*>(sender());
1385     if(item)
1386         itemChanged(item);
1387 }
1388 
itemChanged(Item_id i)1389 void Scene::itemChanged(Item_id i)
1390 {
1391   if(dont_emit_changes)
1392     return;
1393   if(i < 0 || i >= m_entries.size())
1394     return;
1395 
1396   Q_EMIT dataChanged(this->createIndex(i, 0),
1397                      this->createIndex(i, LastColumn));
1398 }
1399 
itemChanged(CGAL::Three::Scene_item * item)1400 void Scene::itemChanged(CGAL::Three::Scene_item*item )
1401 {
1402   if(dont_emit_changes)
1403     return;
1404   itemChanged(item_id(item));
1405 }
1406 
allItemsChanged()1407 void Scene::allItemsChanged()
1408 {
1409   Q_EMIT dataChanged(this->createIndex(0, 0),
1410                      this->createIndex(m_entries.size() - 1, LastColumn));
1411 }
1412 
itemVisibilityChanged()1413 void Scene::itemVisibilityChanged()
1414 {
1415     CGAL::Three::Scene_item* item = qobject_cast<CGAL::Three::Scene_item*>(sender());
1416     if(item)
1417         itemVisibilityChanged(item);
1418 }
1419 
itemVisibilityChanged(CGAL::Three::Scene_item * item)1420 void Scene::itemVisibilityChanged(CGAL::Three::Scene_item* item)
1421 {
1422   if(item->isFinite()
1423      && !item->isEmpty())
1424   {
1425     //does not recenter
1426     if(visibility_recentering_enabled){
1427       Q_EMIT updated_bbox(true);
1428 
1429     }
1430   }
1431 }
1432 
1433 
editorEvent(QEvent * event,QAbstractItemModel * model,const QStyleOptionViewItem & option,const QModelIndex & index)1434 bool SceneDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
1435                                 const QStyleOptionViewItem &option,
1436                                 const QModelIndex &index)
1437 {
1438     QAbstractProxyModel* proxyModel = dynamic_cast<QAbstractProxyModel*>(model);
1439     Q_ASSERT(proxyModel);
1440     Scene *scene = dynamic_cast<Scene*>(proxyModel->sourceModel());
1441     Q_ASSERT(scene);
1442     int id = scene->index_map[proxyModel->mapToSource(index)];
1443     switch(index.column()) {
1444     case Scene::VisibleColumn:
1445         if (event->type() == QEvent::MouseButtonPress) {
1446             QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
1447             if(mouseEvent->button() == ::Qt::LeftButton) {
1448                 int x = mouseEvent->pos().x() - option.rect.x();
1449                 if(x >= (option.rect.width() - size)/2 &&
1450                         x <= (option.rect.width() + size)/2) {
1451                     model->setData(index, !model->data(index).toBool());
1452                 }
1453             }
1454             return false; //so that the selection can change
1455         }
1456         return true;
1457         break;
1458     case Scene::ColorColumn:
1459         if (event->type() == QEvent::MouseButtonPress) {
1460             QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
1461             if(mouseEvent->button() == ::Qt::LeftButton) {
1462                 QColor color =
1463                         QColorDialog::getColor(model->data(index).value<QColor>(),
1464                                                nullptr/*,
1465                                                tr("Select color"),
1466                                                QColorDialog::ShowAlphaChannel*/);
1467                 if (color.isValid()) {
1468                     model->setData(index, color );
1469                 }
1470             }
1471         }
1472         else if(event->type() == QEvent::MouseButtonDblClick) {
1473             return true; // block double-click
1474         }
1475         return false;
1476         break;
1477     case Scene::RenderingModeColumn:
1478         if (event->type() == QEvent::MouseButtonPress) {
1479             QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
1480             if(mouseEvent->button() == ::Qt::LeftButton) {
1481                 // Switch rendering mode
1482                 /*RenderingMode*/int rendering_mode = model->data(index, ::Qt::EditRole).toInt();
1483                 rendering_mode = (rendering_mode+1) % NumberOfRenderingMode;
1484                 model->setData(index, rendering_mode);
1485             }
1486         }
1487         else if(event->type() == QEvent::MouseButtonDblClick) {
1488             return true; // block double-click
1489         }
1490         return false;
1491         break;
1492     case Scene::ABColumn:
1493         if (event->type() == QEvent::MouseButtonPress) {
1494             if(id == scene->item_B) {
1495                 scene->item_A = id;
1496                 scene->item_B = -1;
1497             }
1498             else if(id == scene->item_A) {
1499                 scene->item_B = id;
1500                 scene->item_A = -1;
1501             }
1502             else if(scene->item_A == -1) {
1503                 scene->item_A = id;
1504             }
1505             else {
1506                 scene->item_B = id;
1507             }
1508             scene->dataChanged(scene->createIndex(0, Scene::ABColumn),
1509                                scene->createIndex(scene->rowCount() - 1, Scene::ABColumn));
1510         }
1511         return false;
1512         break;
1513     default:
1514         return QItemDelegate::editorEvent(event, model, option, index);
1515     }
1516 }
1517 
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const1518 void SceneDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
1519                           const QModelIndex &index) const
1520 {
1521     QModelIndex test = proxy->mapToSource(index);
1522     if (index.column() != Scene::VisibleColumn) {
1523         QItemDelegate::paint(painter, option, index);
1524     } else {
1525         const QAbstractItemModel *model = index.model();
1526 
1527         QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ?
1528                     (option.state & QStyle::State_Active) ? QPalette::Normal : QPalette::Inactive : QPalette::Disabled;
1529 
1530         if (option.state & QStyle::State_Selected)
1531             painter->fillRect(option.rect, option.palette.color(cg, QPalette::Highlight));
1532         bool checked = model->data(index, ::Qt::DisplayRole).toBool();
1533         int width = option.rect.width();
1534         int height = option.rect.height();
1535         size = (std::min)(width, height);
1536         int x = option.rect.x() + (option.rect.width() / 2) - (size / 2);;
1537         int y = option.rect.y() + (option.rect.height() / 2) - (size / 2);
1538         if(test.row()>=0 && test.row()<scene->m_entries.size()){
1539 
1540             if(checked) {
1541                 painter->drawPixmap(x, y, checkOnPixmap.scaled(QSize(size, size),
1542                                                                ::Qt::KeepAspectRatio,
1543                                                                ::Qt::SmoothTransformation));
1544             }
1545             else {
1546                 painter->drawPixmap(x, y, checkOffPixmap.scaled(QSize(size, size),
1547                                                                 ::Qt::KeepAspectRatio,
1548                                                                 ::Qt::SmoothTransformation));
1549             }
1550         }
1551         drawFocus(painter, option, option.rect); // since we draw the grid ourselves
1552     }
1553 }
1554 
setItemVisible(int index,bool b)1555 void Scene::setItemVisible(int index, bool b)
1556 {
1557     if( index < 0 || index >= m_entries.size() )
1558         return;
1559     m_entries[index]->setVisible(b);
1560   Q_EMIT dataChanged(this->createIndex(index, VisibleColumn),
1561                      this->createIndex(index, VisibleColumn));
1562 }
1563 
setSelectionRay(double orig_x,double orig_y,double orig_z,double dir_x,double dir_y,double dir_z)1564 void Scene::setSelectionRay(double orig_x,
1565                             double orig_y,
1566                             double orig_z,
1567                             double dir_x,
1568                             double dir_y,
1569                             double dir_z)
1570 {
1571     CGAL::Three::Scene_item* item = this->item(selected_item);
1572     if(item) item->select(orig_x,
1573                           orig_y,
1574                           orig_z,
1575                           dir_x,
1576                           dir_y,
1577                           dir_z);
1578 }
1579 
setItemA(int i)1580 void Scene::setItemA(int i)
1581 {
1582     item_A = i;
1583     if(item_A == item_B)
1584     {
1585         item_B = -1;
1586     }
1587   Q_EMIT dataChanged(this->createIndex(0, ABColumn),
1588                      this->createIndex(m_entries.size()-1, ABColumn));
1589 }
1590 
setItemB(int i)1591 void Scene::setItemB(int i)
1592 {
1593     item_B = i;
1594     if(item_A == item_B)
1595     {
1596         item_A = -1;
1597     }
1598   Q_EMIT updated();
1599   Q_EMIT dataChanged(this->createIndex(0, ABColumn),
1600                      this->createIndex(m_entries.size()-1, ABColumn));
1601 }
1602 
bbox() const1603 Scene::Bbox Scene::bbox() const
1604 {
1605     if(m_entries.empty())
1606         return Bbox(0,0,0,0,0,0);
1607 
1608     bool bbox_initialized = false;
1609     Bbox bbox = Bbox(0,0,0,0,0,0);
1610     Q_FOREACH(CGAL::Three::Scene_item* item, m_entries)
1611     {
1612         if(item->isFinite() && !item->isEmpty() && item->visible()) {
1613             if(bbox_initialized) {
1614 
1615                 bbox = bbox + item->bbox();
1616             }
1617             else {
1618                 bbox = item->bbox();
1619                 bbox_initialized = true;
1620 
1621             }
1622         }
1623 
1624     }
1625     return bbox;
1626 }
1627 
item_entries() const1628 QList<Scene_item*> Scene::item_entries() const
1629 {
1630     return m_entries;
1631 }
redraw_model()1632 void Scene::redraw_model()
1633 {
1634     //makes the hierarchy in the tree
1635     //clears the model
1636     clear();
1637     index_map.clear();
1638     //fills the model
1639     Q_FOREACH(Item_id id, children)
1640     {
1641         organize_items(m_entries[id], invisibleRootItem(), 0);
1642     }
1643     Q_EMIT restoreCollapsedState();
1644 }
changeGroup(Scene_item * item,CGAL::Three::Scene_group_item * target_group)1645 void Scene::changeGroup(Scene_item *item, CGAL::Three::Scene_group_item *target_group)
1646 {
1647     //remove item from the containing group if any
1648     if(item->parentGroup())
1649     {
1650       if(item->parentGroup()->isChildLocked(item))
1651         return;
1652       item->parentGroup()->removeChild(item);
1653       children.push_back(item_id(item));
1654     }
1655       else
1656       {
1657         children.removeAll(item_id(item));
1658       }
1659     //add the item to the target group
1660     target_group->addChild(item);
1661     item->moveToGroup(target_group);
1662     redraw_model();
1663     Q_EMIT updated();
1664 }
1665 
printPrimitiveId(QPoint point,CGAL::Three::Viewer_interface * viewer)1666 void Scene::printPrimitiveId(QPoint point, CGAL::Three::Viewer_interface* viewer)
1667 {
1668   Scene_item *it = item(mainSelectionIndex());
1669   if(it)
1670   {
1671     //Only call printPrimitiveId if the item is a Scene_print_item_interface
1672     Scene_print_item_interface* item= qobject_cast<Scene_print_item_interface*>(it);
1673     if(item)
1674       item->printPrimitiveId(point, viewer);
1675   }
1676 }
printVertexIds()1677 void Scene::printVertexIds()
1678 {
1679   Scene_item *it = item(mainSelectionIndex());
1680   if(it)
1681   {
1682     Scene_print_item_interface* item= qobject_cast<Scene_print_item_interface*>(it);
1683     if(item)
1684       item->printVertexIds();
1685   }
1686 }
1687 
printEdgeIds()1688 void Scene::printEdgeIds()
1689 {
1690   Scene_item *it = item(mainSelectionIndex());
1691   if(it)
1692   {
1693     //Only call printEdgeIds if the item is a Scene_print_item_interface
1694     Scene_print_item_interface* item= qobject_cast<Scene_print_item_interface*>(it);
1695     if(item)
1696       item->printEdgeIds();
1697   }
1698 }
1699 
printFaceIds()1700 void Scene::printFaceIds()
1701 {
1702   Scene_item *it = item(mainSelectionIndex());
1703   if(it)
1704   {
1705     //Only call printFaceIds if the item is a Scene_print_item_interface
1706     Scene_print_item_interface* item= qobject_cast<Scene_print_item_interface*>(it);
1707     if(item)
1708       item->printFaceIds();
1709   }
1710 }
1711 
printAllIds()1712 void Scene::printAllIds()
1713 {
1714   Scene_item *it = item(mainSelectionIndex());
1715   if(it)
1716   {
1717     //Only call printFaceIds if the item is a Scene_print_item_interface
1718     Scene_print_item_interface* item= qobject_cast<Scene_print_item_interface*>(it);
1719     if(item)
1720       item->printAllIds();
1721   }
1722 }
updatePrimitiveIds(CGAL::Three::Scene_item * it)1723 void Scene::updatePrimitiveIds(CGAL::Three::Scene_item* it)
1724 {
1725   if(it)
1726   {
1727     Scene_print_item_interface* item= qobject_cast<Scene_print_item_interface*>(it);
1728     if(item)
1729     {
1730       //As this function works as a toggle, the first call hides the ids and the second one shows them again,
1731       //thereby triggering their re-computation.
1732       item->printVertexIds();
1733       item->printVertexIds();
1734 
1735       item->printEdgeIds();
1736       item->printEdgeIds();
1737 
1738       item->printFaceIds();
1739       item->printFaceIds();
1740     }
1741   }
1742 }
testDisplayId(double x,double y,double z,CGAL::Three::Viewer_interface * viewer)1743 bool Scene::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer)
1744 {
1745     CGAL::Three::Scene_item *i = item(mainSelectionIndex());
1746     if(!i)
1747       return false;
1748     Scene_print_item_interface* spit= qobject_cast<Scene_print_item_interface*>(i);
1749     if(spit && i->visible())
1750     {
1751         bool res = spit->testDisplayId(x,y,z, viewer);
1752         return res;
1753     }
1754     else
1755       return false;
1756 }
1757 #include "Scene_find_items.h"
1758 
organize_items(Scene_item * item,QStandardItem * root,int loop)1759 void Scene::organize_items(Scene_item* item, QStandardItem* root, int loop)
1760 {
1761     if(item->has_group <= loop)
1762     {
1763         QList<QStandardItem*> list;
1764         for(int i=0; i<5; i++)
1765         {
1766             list<<new QStandardItem();
1767             list.at(i)->setEditable(false);
1768 
1769         }
1770         root->appendRow(list);
1771         for(int i=0; i<5; i++){
1772             index_map[list.at(i)->index()] = m_entries.indexOf(item);
1773         }
1774         CGAL::Three::Scene_group_item* group =
1775                 qobject_cast<CGAL::Three::Scene_group_item*>(item);
1776         if(group)
1777         {
1778           Q_FOREACH(Item_id id, group->getChildren())
1779           {
1780             CGAL::Three::Scene_item* child = group->getChild(id);
1781                 organize_items(child, list.first(), loop+1);
1782             }
1783         }
1784     }
1785 }
1786 
setExpanded(QModelIndex id)1787 void Scene::setExpanded(QModelIndex id)
1788 {
1789     CGAL::Three::Scene_group_item* group =
1790             qobject_cast<CGAL::Three::Scene_group_item*>(item(getIdFromModelIndex(id)));
1791     if(group)
1792     {
1793         group->setExpanded(true);
1794     }
1795 }
setCollapsed(QModelIndex id)1796 void Scene::setCollapsed(QModelIndex id)
1797 {
1798     CGAL::Three::Scene_group_item* group =
1799             qobject_cast<CGAL::Three::Scene_group_item*>(item(getIdFromModelIndex(id)));
1800     if(group)
1801     {
1802         group->setExpanded(false);
1803     }
1804 }
1805 
getIdFromModelIndex(QModelIndex modelId) const1806 int Scene::getIdFromModelIndex(QModelIndex modelId)const
1807 {
1808     return index_map.value(modelId);
1809 }
1810 
getModelIndexFromId(int id) const1811 QList<QModelIndex> Scene::getModelIndexFromId(int id) const
1812 {
1813     return index_map.keys(id);
1814 }
1815 
addGroup(Scene_group_item * group)1816 void Scene::addGroup(Scene_group_item* group)
1817 {
1818     connect(this, SIGNAL(drawFinished()), group, SLOT(resetDraw()));
1819     connect(this, SIGNAL(indexErased(Scene_interface::Item_id)),
1820                 group, SLOT(adjustIds(Scene_interface::Item_id)));
1821 }
1822 
1823 namespace scene { namespace details {
1824 
1825 Q_DECL_EXPORT
1826 CGAL::Three::Scene_item*
findItem(const CGAL::Three::Scene_interface * scene_interface,const QMetaObject & metaobj,QString name,Scene_item_name_fn_ptr fn)1827 findItem(const CGAL::Three::Scene_interface* scene_interface,
1828          const QMetaObject& metaobj,
1829          QString name, Scene_item_name_fn_ptr fn) {
1830     const Scene* scene = dynamic_cast<const Scene*>(scene_interface);
1831     if(!scene) return nullptr;
1832     Q_FOREACH(CGAL::Three::Scene_item* item, scene->entries()) {
1833        CGAL::Three::Scene_item* ptr = qobject_cast<CGAL::Three::Scene_item*>(metaobj.cast(item));
1834         if(ptr && ((ptr->*fn)() == name)) return ptr;
1835     }
1836     return nullptr;
1837 }
1838 
1839 Q_DECL_EXPORT
1840 QList<CGAL::Three::Scene_item*>
findItems(const CGAL::Three::Scene_interface * scene_interface,const QMetaObject &,QString name,Scene_item_name_fn_ptr fn)1841 findItems(const CGAL::Three::Scene_interface* scene_interface,
1842 
1843           const QMetaObject&,
1844           QString name, Scene_item_name_fn_ptr fn)
1845 {
1846     const Scene* scene = dynamic_cast<const Scene*>(scene_interface);
1847     QList<CGAL::Three::Scene_item*> list;
1848     if(!scene) return list;
1849 
1850     Q_FOREACH(CGAL::Three::Scene_item* item, scene->entries()) {
1851         CGAL::Three::Scene_item* ptr = qobject_cast<CGAL::Three::Scene_item*>(item);
1852         if(ptr && ((ptr->*fn)() == name)) {
1853             list << ptr;
1854         }
1855     }
1856     return list;
1857 }
1858 
1859 } // end namespace details
1860                 } // end namespace scene
1861 
zoomToPosition(QPoint point,Viewer_interface * viewer)1862 void Scene::zoomToPosition(QPoint point, Viewer_interface *viewer)
1863 {
1864   for(int i=0; i<numberOfEntries(); ++i)
1865   {
1866     if(!item(i)->visible())
1867       continue;
1868     Scene_zoomable_item_interface* zoom_item = qobject_cast<Scene_zoomable_item_interface*>(item(i));
1869     if(zoom_item)
1870     {
1871       zoom_item->zoomToPosition(point, viewer);
1872     }
1873   }
1874 }
1875 
adjustIds(Item_id removed_id)1876 void Scene::adjustIds(Item_id removed_id)
1877 {
1878   for(int i = 0; i < children.size(); ++i)
1879   {
1880     if(children[i] >= removed_id)
1881       --children[i];
1882   }
1883   for(int i = removed_id; i < numberOfEntries(); ++i)
1884   {
1885     m_entries[i]->setId(i-1);//the signal is emitted before m_entries is amputed from the item, so new id is current id -1.
1886   }
1887 }
1888 
computeBbox()1889 void Scene::computeBbox()
1890 {
1891   if(m_entries.empty())
1892   {
1893     last_bbox = Bbox(0,0,0,0,0,0);
1894     return;
1895   }
1896 
1897   bool bbox_initialized = false;
1898   Bbox bbox = Bbox(0,0,0,0,0,0);
1899   Q_FOREACH(CGAL::Three::Scene_item* item, m_entries)
1900   {
1901     if(item->isFinite() && !item->isEmpty() ) {
1902       if(bbox_initialized) {
1903 
1904         bbox = bbox + item->bbox();
1905       }
1906       else {
1907         bbox = item->bbox();
1908         bbox_initialized = true;
1909 
1910       }
1911     }
1912 
1913   }
1914   last_bbox = bbox;
1915 }
1916 
newViewer(Viewer_interface * viewer)1917 void Scene::newViewer(Viewer_interface *viewer)
1918 {
1919   initGL(viewer);
1920   Q_FOREACH(Scene_item* item, m_entries)
1921   {
1922     item->newViewer(viewer);
1923   }
1924 }
1925 
removeViewer(Viewer_interface * viewer)1926 void Scene::removeViewer(Viewer_interface *viewer)
1927 {
1928  //already destroyed;
1929   if(viewer->property("is_destroyed").toBool())
1930     return;
1931 
1932   vaos[viewer]->destroy();
1933   vaos[viewer]->deleteLater();
1934   vaos.remove(viewer);
1935   Q_FOREACH(Scene_item* item, m_entries)
1936   {
1937     item->removeViewer(viewer);
1938   }
1939 }
1940 
initGL(Viewer_interface * viewer)1941 void Scene::initGL(Viewer_interface *viewer)
1942 {
1943   viewer->makeCurrent();
1944   vaos[viewer] = new QOpenGLVertexArrayObject();
1945   vaos[viewer]->create();
1946   program.bind();
1947   vaos[viewer]->bind();
1948   vbo[0].bind();
1949   vbo[0].allocate(points, 18 * sizeof(float));
1950   program.enableAttributeArray("vertex");
1951   program.setAttributeArray("vertex", GL_FLOAT, nullptr, 3);
1952   vbo[0].release();
1953 
1954   vbo[1].bind();
1955   vbo[1].allocate(uvs, 12 * sizeof(float));
1956   program.enableAttributeArray("v_texCoord");
1957   program.setAttributeArray("v_texCoord", GL_FLOAT, nullptr, 2);
1958   vbo[1].release();
1959   vaos[viewer]->release();
1960   program.release();
1961 }
1962 
callDraw()1963 void Scene::callDraw(){
1964   Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool())
1965   {
1966     qobject_cast<Viewer_interface*>(v)->update();
1967   }
1968 }
1969 
enableVisibilityRecentering(bool b)1970 void Scene::enableVisibilityRecentering(bool b)
1971 {
1972   visibility_recentering_enabled = b;
1973 }
1974 
addChild(Scene_item * item)1975 void Scene::addChild(Scene_item *item)
1976 {
1977   children.push_back(item_id(item));
1978 }
1979