ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvDBRoot.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "ecvDBRoot.h"
9 
10 // Qt
11 #include <QtCompat.h>
12 
13 #include <QApplication>
14 #include <QHeaderView>
15 #include <QInputDialog>
16 #include <QMenu>
17 #include <QMessageBox>
18 #include <QMimeData>
19 #include <QSettings>
20 #include <QStandardItemModel>
21 #include <QTreeView>
22 
23 // LOCAL
24 #include "MainWindow.h"
25 #include "ecvProgressDialog.h"
27 #include "ecvSelectChildrenDlg.h"
28 
29 // CV_CORE_LIB
30 #include <CVLog.h>
31 #include <CVMiscTools.h>
32 
33 // CV_DB_LIB
34 #include <ecv2DLabel.h>
35 #include <ecv2DViewportLabel.h>
36 #include <ecvDisplayTools.h>
37 #include <ecvFacet.h>
38 #include <ecvGBLSensor.h>
39 #include <ecvGenericPointCloud.h>
40 #include <ecvGenericPrimitive.h>
41 #include <ecvHObject.h>
42 #include <ecvHObjectCaster.h>
43 #include <ecvImage.h>
44 #include <ecvMaterialSet.h>
45 #include <ecvMesh.h>
46 #include <ecvPlane.h>
47 #include <ecvPointCloud.h>
48 #include <ecvPolyline.h>
49 #include <ecvScalarField.h>
50 
51 // common
52 #include <ecvPickOneElementDlg.h>
53 
54 // system
55 #include <algorithm>
56 #include <cassert>
57 #include <cstring>
58 
59 // Minimum width of the left column of the properties tree view
60 static const int c_propViewLeftColumnWidth = 115;
61 
62 // test whether a cloud can be deleted or moved
63 static bool CanDetachCloud(const ccHObject* obj) {
64  if (!obj) {
65  assert(false);
66  return false;
67  }
68 
69  ccHObject* parent = obj->getParent();
70  if (!parent) {
71  assert(false);
72  return true;
73  }
74 
75  // can't delete the vertices of a mesh or the verties of a polyline
76  bool blocked =
77  ((parent->isKindOf(CV_TYPES::MESH) &&
79  obj)) ||
80  (parent->isKindOf(CV_TYPES::POLY_LINE) &&
81  (dynamic_cast<ccPointCloud*>(ccHObjectCaster::ToPolyline(parent)
82  ->getAssociatedCloud()) ==
83  obj)));
84 
85  return !blocked;
86 }
87 
88 class DBRootIcons {
89 public:
90  const QIcon& icon(CV_CLASS_ENUM id, bool locked) {
91  if (mIconMap.isEmpty()) {
92  init();
93  }
94 
95  if (!mIconMap.contains(id)) {
96  return (locked ? mDefaultIcons.second : mDefaultIcons.first);
97  }
98 
99  const int index = mIconMap[id];
100  const IconPair& icons = mIconList[index];
101 
102  if (!locked) {
103  return icons.first;
104  }
105 
106  // If we don't have a locked icon for this class, return the unlocked
107  // one
108  return (icons.second.isNull() ? icons.first : icons.second);
109  }
110 
111 private:
112  using IconPair =
113  QPair<QIcon, QIcon>; // unlocked icon, locked icon (if any)
114  using IconPairList = QVector<IconPair>;
115  using IconMap = QMap<CV_CLASS_ENUM, int>;
116 
117  void init() {
118  // Special case for default - no icon in general, but a lock if locked
119  mDefaultIcons = {
120  {},
121  QIcon(QStringLiteral(":/Resources/images/dbLockSymbol.png"))};
122 
123  const int hObjectIndex = mIconList.count();
124  mIconList.append(
125  {QIcon(QStringLiteral(
126  ":/Resources/images/dbHObjectSymbol.png")),
127  QIcon(QStringLiteral(
128  ":/Resources/images/dbHObjectSymbolLocked.png"))});
129 
130  const int cloudIndex = mIconList.count();
131  mIconList.append(
132  {QIcon(QStringLiteral(":/Resources/images/dbCloudSymbol.png")),
133  QIcon(QStringLiteral(
134  ":/Resources/images/dbCloudSymbolLocked.png"))});
135 
136  const int geomIndex = mIconList.count();
137  mIconList.append(
138  {QIcon(QStringLiteral(
139  ":/Resources/images/dbMiscGeomSymbol.png")),
140  QIcon(QStringLiteral(
141  ":/Resources/images/dbMiscGeomSymbolLocked.png"))});
142 
143  const int meshIndex = mIconList.count();
144  mIconList.append(
145  {QIcon(QStringLiteral(":/Resources/images/dbMeshSymbol.png")),
146  QIcon(QStringLiteral(
147  ":/Resources/images/dbMeshSymbolLocked.png"))});
148 
149  const int subMeshIndex = mIconList.count();
150  mIconList.append(
151  {QIcon(QStringLiteral(
152  ":/Resources/images/dbSubMeshSymbol.png")),
153  QIcon(QStringLiteral(
154  ":/Resources/images/dbSubMeshSymbolLocked.png"))});
155 
156  const int polyLineIndex = mIconList.count();
157  mIconList.append({QIcon(QStringLiteral(
158  ":/Resources/images/dbPolylineSymbol.png")),
159  {}});
160 
161  const int circleIndex = mIconList.count();
162  mIconList.append(
163  {QIcon(QStringLiteral(":/Resources/images/dbCircle.png")), {}});
164 
165  const int octreeIndex = mIconList.count();
166  mIconList.append(
167  {QIcon(QStringLiteral(":/Resources/images/dbOctreeSymbol.png")),
168  QIcon(QStringLiteral(
169  ":/Resources/images/dbOctreeSymbolLocked.png"))});
170 
171  const int calibratedImageIndex = mIconList.count();
172  mIconList.append(
173  {QIcon(QStringLiteral(
174  ":/Resources/images/dbCalibratedImageSymbol.png")),
175  {}});
176 
177  const int imageIndex = mIconList.count();
178  mIconList.append(
179  {QIcon(QStringLiteral(":/Resources/images/dbImageSymbol.png")),
180  {}});
181 
182  const int sensorIndex = mIconList.count();
183  mIconList.append({QIcon(QStringLiteral(
184  ":/Resources/images/dbGBLSensorSymbol.png")),
185  {}});
186 
187  const int cameraSensorIndex = mIconList.count();
188  mIconList.append({QIcon(QStringLiteral(
189  ":/Resources/images/dbCamSensorSymbol.png")),
190  {}});
191 
192  const int materialSetIndex = mIconList.count();
193  mIconList.append({QIcon(QStringLiteral(
194  ":/Resources/images/dbMaterialSymbol.png")),
195  {}});
196 
197  const int containerIndex = mIconList.count();
198  mIconList.append(
199  {QIcon(QStringLiteral(
200  ":/Resources/images/dbContainerSymbol.png")),
201  QIcon(QStringLiteral(
202  ":/Resources/images/dbContainerSymbolLocked.png"))});
203 
204  const int labelIndex = mIconList.count();
205  mIconList.append(
206  {QIcon(QStringLiteral(":/Resources/images/dbLabelSymbol.png")),
207  {}});
208 
209  const int viewportObjIndex = mIconList.count();
210  mIconList.append({QIcon(QStringLiteral(
211  ":/Resources/images/dbViewportSymbol.png")),
212  {}});
213 
214  const int viewportLabelIndex = mIconList.count();
215  mIconList.append({QIcon(QStringLiteral(
216  ":/Resources/images/dbAreaLabelSymbol.png")),
217  {}});
218 
219  mIconMap = {{CV_TYPES::HIERARCHY_OBJECT, hObjectIndex},
220  {CV_TYPES::POINT_CLOUD, cloudIndex},
221  {CV_TYPES::PLANE, geomIndex},
222  {CV_TYPES::SPHERE, geomIndex},
223  {CV_TYPES::TORUS, geomIndex},
224  {CV_TYPES::CYLINDER, geomIndex},
225  {CV_TYPES::CONE, geomIndex},
226  {CV_TYPES::BOX, geomIndex},
227  {CV_TYPES::DISH, geomIndex},
228  {CV_TYPES::EXTRU, geomIndex},
229  {CV_TYPES::FACET, geomIndex},
230  {CV_TYPES::QUADRIC, geomIndex},
231  {CV_TYPES::MESH, meshIndex},
232  {CV_TYPES::MESH_GROUP, subMeshIndex},
233  {CV_TYPES::SUB_MESH, subMeshIndex},
234  {CV_TYPES::POLY_LINE, polyLineIndex},
235  {CV_TYPES::CIRCLE, circleIndex},
236  {CV_TYPES::POINT_OCTREE, octreeIndex},
237  {CV_TYPES::CALIBRATED_IMAGE, calibratedImageIndex},
238  {CV_TYPES::IMAGE, imageIndex},
239  {CV_TYPES::SENSOR, sensorIndex},
240  {CV_TYPES::GBL_SENSOR, sensorIndex},
241  {CV_TYPES::CAMERA_SENSOR, cameraSensorIndex},
242  {CV_TYPES::MATERIAL_SET, materialSetIndex},
243  {CV_TYPES::NORMALS_ARRAY, containerIndex},
244  {CV_TYPES::NORMAL_INDEXES_ARRAY, containerIndex},
245  {CV_TYPES::RGB_COLOR_ARRAY, containerIndex},
246  {CV_TYPES::TEX_COORDS_ARRAY, containerIndex},
247  {CV_TYPES::TRANS_BUFFER, containerIndex},
248  {CV_TYPES::LABEL_2D, labelIndex},
249  {CV_TYPES::VIEWPORT_2D_OBJECT, viewportObjIndex},
250  {CV_TYPES::VIEWPORT_2D_LABEL, viewportLabelIndex},
251  {CV_TYPES::COORDINATESYSTEM, geomIndex},
252  {CV_TYPES::DISC, geomIndex}};
253  }
254 
255  IconPair mDefaultIcons;
256  IconPairList mIconList;
257  IconMap mIconMap;
258 };
259 
260 Q_GLOBAL_STATIC(DBRootIcons, gDBRootIcons)
261 
263  QTreeView* propertiesTreeWidget,
264  QObject* parent)
265  : QAbstractItemModel(parent) {
266  m_treeRoot = new ccHObject("DB Tree");
267 
268  // DB Tree
269  assert(dbTreeWidget);
270  m_dbTreeWidget = dbTreeWidget;
271  m_dbTreeWidget->setModel(this);
272  m_dbTreeWidget->header()->hide();
273 
274  // drag & drop support
275  m_dbTreeWidget->setDragEnabled(true);
276  m_dbTreeWidget->setAcceptDrops(true);
277  m_dbTreeWidget->setDropIndicatorShown(true);
278 
279  // context menu on DB tree elements
280  m_dbTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
281  m_expandBranch = new QAction(tr("Expand branch"), this);
282  m_collapseBranch = new QAction(tr("Collapse branch"), this);
283  m_gatherInformation = new QAction(tr("Information (recursive)"), this);
284  m_sortChildrenType = new QAction(tr("Sort children by type"), this);
285  m_sortChildrenAZ = new QAction(tr("Sort children by name (A-Z)"), this);
286  m_sortChildrenZA = new QAction(tr("Sort children by name (Z-A)"), this);
287  m_selectByTypeAndName =
288  new QAction(tr("Select children by type and/or name"), this);
289  m_exportImages = new QAction(tr("Export images"), this);
290  m_deleteSelectedEntities = new QAction(tr("Delete"), this);
291  m_toggleSelectedEntities = new QAction(tr("Toggle"), this);
292  m_toggleSelectedEntitiesVisibility =
293  new QAction(tr("Toggle visibility"), this);
294  m_toggleSelectedEntitiesColor = new QAction(tr("Toggle color"), this);
295  m_toggleSelectedEntitiesNormals = new QAction(tr("Toggle normals"), this);
296  m_toggleSelectedEntitiesMat =
297  new QAction(tr("Toggle materials/textures"), this);
298  m_toggleSelectedEntitiesSF = new QAction(tr("Toggle SF"), this);
299  m_toggleSelectedEntities3DName = new QAction(tr("Toggle 3D name"), this);
300  m_addEmptyGroup = new QAction(tr("Add empty group"), this);
301  m_alignCameraWithEntity = new QAction(tr("Align camera"), this);
302  m_alignCameraWithEntityReverse =
303  new QAction(tr("Align camera (reverse)"), this);
304  m_enableBubbleViewMode = new QAction(tr("Bubble-view"), this);
305  m_editLabelScalarValue = new QAction(tr("Edit scalar value"), this);
306 
307  m_contextMenuPos = QPoint(-1, -1);
308 
309  // connect custom context menu actions
310  connect(m_dbTreeWidget, &QWidget::customContextMenuRequested, this,
312  connect(m_expandBranch, &QAction::triggered, this, &ccDBRoot::expandBranch);
313  connect(m_collapseBranch, &QAction::triggered, this,
315  connect(m_gatherInformation, &QAction::triggered, this,
317  connect(m_sortChildrenAZ, &QAction::triggered, this,
319  connect(m_sortChildrenZA, &QAction::triggered, this,
321  connect(m_sortChildrenType, &QAction::triggered, this,
323  connect(m_selectByTypeAndName, &QAction::triggered, this,
325  connect(m_exportImages, &QAction::triggered, this, &ccDBRoot::exportImages);
326  connect(m_deleteSelectedEntities, &QAction::triggered, this,
328  connect(m_toggleSelectedEntities, &QAction::triggered, this,
330  connect(m_toggleSelectedEntitiesVisibility, &QAction::triggered, this,
332  connect(m_toggleSelectedEntitiesColor, &QAction::triggered, this,
334  connect(m_toggleSelectedEntitiesNormals, &QAction::triggered, this,
336  connect(m_toggleSelectedEntitiesMat, &QAction::triggered, this,
338  connect(m_toggleSelectedEntitiesSF, &QAction::triggered, this,
340  connect(m_toggleSelectedEntities3DName, &QAction::triggered, this,
342  connect(m_addEmptyGroup, &QAction::triggered, this,
344  connect(m_alignCameraWithEntity, &QAction::triggered, this,
346  connect(m_alignCameraWithEntityReverse, &QAction::triggered, this,
348  connect(m_enableBubbleViewMode, &QAction::triggered, this,
350  connect(m_editLabelScalarValue, &QAction::triggered, this,
352 
353  // other DB tree signals/slots connection
354  connect(m_dbTreeWidget->selectionModel(),
355  &QItemSelectionModel::selectionChanged, this,
357 
358  // Properties Tree
359  assert(propertiesTreeWidget);
360  m_propertiesTreeWidget = propertiesTreeWidget;
361  m_propertiesModel = new QStandardItemModel(0, 2, parent);
362  m_ccPropDelegate = new ccPropertiesTreeDelegate(m_propertiesModel,
363  m_propertiesTreeWidget);
364  m_propertiesTreeWidget->setItemDelegate(m_ccPropDelegate);
365  m_propertiesTreeWidget->setModel(m_propertiesModel);
366  m_propertiesTreeWidget->header()->setSectionResizeMode(
367  QHeaderView::Interactive);
368  m_propertiesTreeWidget->setEnabled(false);
369 
370  // Properties tree signals/slots connection
371  connect(m_ccPropDelegate,
374  connect(m_ccPropDelegate,
377  connect(m_ccPropDelegate,
380 }
381 
383  delete m_ccPropDelegate;
384  delete m_propertiesModel;
385  delete m_treeRoot;
386 }
387 
389  m_propertiesTreeWidget->hide();
390  m_dbTreeWidget->hide();
391 }
392 
394  m_propertiesTreeWidget->show();
395  m_dbTreeWidget->show();
396 }
397 
399  if (!m_treeRoot) {
400  return;
401  }
402 
403  while (m_treeRoot->getChildrenNumber() > 0) {
404  int i = static_cast<int>(m_treeRoot->getChildrenNumber()) - 1;
405  ccHObject* object = m_treeRoot->getChild(static_cast<unsigned int>(i));
406  assert(object);
407 
408  beginRemoveRows(index(object).parent(), i, i);
410  endRemoveRows();
411  }
412 
413  emit dbIsEmpty();
414 
416 
419 }
420 
422 
423 void ccDBRoot::addElement(ccHObject* object, bool autoExpand /*=true*/) {
424  if (!m_treeRoot) {
425  assert(false);
426  return;
427  }
428  if (!object) {
429  assert(false);
430  return;
431  }
432 
433  bool wasEmpty = (m_treeRoot->getChildrenNumber() == 0);
434 
435  // look for object's parent
436  ccHObject* parentObject = object->getParent();
437  if (!parentObject) {
438  // if the object has no parent, it will be inserted at tree root
439  parentObject = m_treeRoot;
440  m_treeRoot->addChild(object);
441  } else {
442  // DGM TODO: how could we check that the object is not already inserted
443  // in the DB tree? The double insertion can cause serious damage to it
444  // (not sure why excatly though).
445 
446  // The code below doesn't work because the 'index' method will always
447  // return a valid index as soon as the object has a parent (index
448  // creation is a purely 'logical' approach) QModelIndex nodeIndex =
449  // index(object); if (nodeIndex.isValid()) return;
450  }
451 
452  // look for insert node index in tree
453  QModelIndex insertNodeIndex = index(parentObject);
454  int childPos = parentObject->getChildIndex(object);
455 
456  // row insertion operation (start)
457  beginInsertRows(insertNodeIndex, childPos, childPos);
458 
459  // row insertion operation (end)
460  endInsertRows();
461 
462  if (autoExpand) {
463  // expand the parent (just in case)
464  m_dbTreeWidget->expand(index(parentObject));
465  // and the child
466  m_dbTreeWidget->expand(index(object));
467  } else // if (parentObject)
468  {
469  m_dbTreeWidget->expand(insertNodeIndex);
470  }
471 
472  if (wasEmpty && m_treeRoot->getChildrenNumber() != 0) {
473  emit dbIsNotEmptyAnymore();
474  }
475 }
476 
477 void ccDBRoot::expandElement(ccHObject* object, bool state) {
478  if (!object || object->getChildrenNumber() == 0 || !m_dbTreeWidget) {
479  return;
480  }
481 
482  ccHObject* item = object;
483 
484  // we recursively expand sub-branches
485  ccHObject::Container toExpand;
486  try {
487  toExpand.push_back(item);
488  while (!toExpand.empty()) {
489  item = toExpand.back();
490  toExpand.pop_back();
491 
492  QModelIndex itemIndex = index(item);
493  if (itemIndex.isValid()) {
494  if (state)
495  m_dbTreeWidget->expand(itemIndex);
496  else
497  m_dbTreeWidget->collapse(itemIndex);
498  }
499 
500  assert(item->getChildrenNumber() != 0);
501  for (unsigned i = 0; i < item->getChildrenNumber(); ++i) {
502  if (item->getChild(i)->getChildrenNumber() != 0)
503  toExpand.push_back(item->getChild(i));
504  }
505  }
506  } catch (const std::bad_alloc&) {
507  // not enough memory!
508  }
509 
510  m_dbTreeWidget->setExpanded(index(object), state);
511 }
512 
514  if (objects.empty()) {
515  assert(false);
516  return;
517  }
518 
519  // we hide properties view in case this is the deleted object that is
520  // currently selected
522 
523  // every object in tree must have a parent!
524  for (ccHObject* object : objects) {
525  ccHObject* parent = object->getParent();
526  if (!parent) {
527  CVLog::Warning(tr("[ccDBRoot::removeElements] Internal error: "
528  "object '%1' has no parent")
529  .arg(object->getName()));
530  continue;
531  }
532 
533  int childPos = parent->getChildIndex(object);
534  assert(childPos >= 0);
535  {
536  // row removal operation (start)
537  beginRemoveRows(index(parent), childPos, childPos);
538 
539  parent->removeChild(childPos);
540 
541  // row removal operation (end)
542  endRemoveRows();
543  }
544  }
545 
546  // we restore properties view
548 
549  if (m_treeRoot->getChildrenNumber() == 0) {
550  emit dbIsEmpty();
551  }
552 }
553 
555  if (!object) {
556  assert(false);
557  return;
558  }
559 
560  // we hide properties view in case this is the deleted object that is
561  // currently selected
563 
564  // every object in tree must have a parent!
565  ccHObject* parent = object->getParent();
566  if (!parent) {
568  tr("[ccDBRoot::removeElement] Internal error: object has no "
569  "parent"));
570  return;
571  }
572 
573  int childPos = parent->getChildIndex(object);
574  assert(childPos >= 0);
575  {
576  // row removal operation (start)
577  beginRemoveRows(index(parent), childPos, childPos);
578 
579  parent->removeChild(childPos);
580 
581  // row removal operation (end)
582  endRemoveRows();
583  }
584 
585  // we restore properties view
587 
588  if (m_treeRoot->getChildrenNumber() == 0) {
589  emit dbIsEmpty();
590  }
591 }
592 
594  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
595  QModelIndexList selectedIndexes = qism->selectedIndexes();
596  if (selectedIndexes.empty()) {
597  return;
598  }
599  unsigned selCount = static_cast<unsigned>(selectedIndexes.size());
600 
602  bool verticesWarningIssued = false;
603 
604  // we remove all objects that are children of other deleted ones!
605  //(otherwise we may delete the parent before the child!)
606  // TODO DGM: not sure this is still necessary with the new dependency
607  // mechanism
608  std::vector<ccHObject*> toBeDeleted;
609  for (unsigned i = 0; i < selCount; ++i) {
610  ccHObject* obj =
611  static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
612  // we don't take care of parent-less objects (i.e. the tree root)
613  if (!obj->getParent() || obj->isLocked()) {
614  CVLog::Warning(tr("Object '%1' can't be deleted this way (locked)")
615  .arg(obj->getName()));
616  continue;
617  }
618 
619  // we don't consider objects that are 'descendent' of others in the
620  // selection
621  bool isDescendent = false;
622  for (unsigned j = 0; j < selCount; ++j) {
623  if (i != j) {
624  ccHObject* otherObj = static_cast<ccHObject*>(
625  selectedIndexes[j].internalPointer());
626  if (otherObj->isAncestorOf(obj)) {
627  isDescendent = true;
628  break;
629  }
630  }
631  }
632 
633  if (!isDescendent) {
634  // last check: mesh vertices
635  if (obj->isKindOf(CV_TYPES::POINT_CLOUD) && !CanDetachCloud(obj)) {
636  if (!verticesWarningIssued) {
638  tr("Vertices can't be deleted without their parent "
639  "mesh"));
640  verticesWarningIssued = true;
641  }
642  continue;
643  }
644 
645  toBeDeleted.push_back(obj);
646  }
647  }
648 
649  qism->clear();
650  std::vector<removeInfo> toBeDeletedInfos;
651  while (!toBeDeleted.empty()) {
652  ccHObject* object = toBeDeleted.back();
653  assert(object);
654  object->getTypeID_recursive(toBeDeletedInfos, true);
655  toBeDeleted.pop_back();
656 
657  if (object->isKindOf(CV_TYPES::MESH)) {
658  // specific case: the object is a mesh and its parent is its
659  // vertices! (can happen if a Delaunay mesh is computed directly in
660  // CC)
661  if (object->getParent() &&
662  object->getParent() == ccHObjectCaster::ToGenericMesh(object)
663  ->getAssociatedCloud()) {
664  object->getParent()->setVisible(true);
665  }
666  }
667 
668  ccHObject* parent = object->getParent();
669  int childPos = parent->getChildIndex(object);
670  assert(childPos >= 0);
671 
672  beginRemoveRows(index(object).parent(), childPos, childPos);
673  parent->removeChild(childPos);
674  endRemoveRows();
675  }
676 
678 
679  if (m_treeRoot->getChildrenNumber() == 0) {
680  emit dbIsEmpty();
681  }
682 
683  if (!toBeDeletedInfos.empty()) {
684  ecvDisplayTools::SetRemoveViewIDs(toBeDeletedInfos);
687  }
688 }
689 
690 QVariant ccDBRoot::data(const QModelIndex& index, int role) const {
691  if (!index.isValid()) {
692  return QVariant();
693  }
694 
695  const ccHObject* item =
696  static_cast<const ccHObject*>(index.internalPointer());
697  assert(item);
698  if (!item) {
699  return QVariant();
700  }
701 
702  switch (role) {
703  case Qt::DisplayRole: {
704  QString baseName(item->getName());
705  if (baseName.isEmpty()) baseName = QStringLiteral("no name");
706  // specific case
707  if (item->isA(CV_TYPES::LABEL_2D))
708  baseName = QStringLiteral("2D label: ") + baseName;
709  else if (item->isA(CV_TYPES::VIEWPORT_2D_LABEL))
710  baseName = QStringLiteral("2D area label: ") + baseName;
711 
712  return baseName;
713  }
714 
715  case Qt::EditRole: {
716  return item->getName();
717  }
718 
719  case Qt::DecorationRole: {
720  // does the object have an "embedded icon"? - It may be the case for
721  // ccHObject defined in plugins
722  QIcon icon = item->getIcon();
723  if (!icon.isNull()) {
724  return icon;
725  }
726 
727  const bool locked = item->isLocked();
728 
729  switch (item->getClassID()) {
731  if (item->getChildrenNumber()) {
732  return gDBRootIcons->icon(item->getClassID(), locked);
733  }
734 
735  return {};
736 
737  default: {
738  return gDBRootIcons->icon(item->getClassID(), locked);
739  }
740  }
741  break;
742  }
743 
744  case Qt::CheckStateRole: {
745  // Don't include checkboxes for hierarchy objects if they have no
746  // children or only contain hierarchy objects (recursively)
747  if (item->getClassID() == CV_TYPES::HIERARCHY_OBJECT) {
748  if (item->getChildrenNumber() == 0) {
749  return {};
750  }
751 
752  ccHObject::Container drawableObjects;
753 
754  unsigned int count =
755  item->filterChildren(drawableObjects, true,
757 
758  if (item->getChildCountRecursive() == count) {
759  return {};
760  }
761  }
762 
763  if (item->isEnabled())
764  return Qt::Checked;
765  else
766  return Qt::Unchecked;
767  }
768 
769  default:
770  // unhandled role
771  break;
772  }
773 
774  return QVariant();
775 }
776 
777 bool ccDBRoot::setData(const QModelIndex& index,
778  const QVariant& value,
779  int role) {
780  if (index.isValid()) {
781  if (role == Qt::EditRole) {
782  if (value.toString().isEmpty()) {
783  return false;
784  }
785 
786  ccHObject* item = static_cast<ccHObject*>(index.internalPointer());
787  assert(item);
788  if (item) {
789  // particular cases:
790  // - labels name is their title (so we update them)
791  // - name might be displayed in 3D
792  if (item->nameShownIn3D() ||
793  item->isKindOf(CV_TYPES::LABEL_2D)) {
794  if (item->isEnabled() && item->isVisible()) {
799  item->getName()));
800  }
801  }
802 
803  item->setName(value.toString());
804 
805  if (item->isEnabled() && item->isVisible()) {
806  MainWindow::TheInstance()->refreshAll(true, false);
807  }
808 
811 
812  emit dataChanged(index, index);
813  }
814 
815  return true;
816  } else if (role == Qt::CheckStateRole) {
817  ccHObject* item = static_cast<ccHObject*>(index.internalPointer());
818  assert(item);
819  if (item) {
820  if (value == Qt::Checked)
821  item->setEnabled(true);
822  else
823  item->setEnabled(false);
824 
825  if (item->isKindOf(CV_TYPES::POINT_OCTREE) ||
827  // rendering this item only
829  item->setRedrawFlagRecursive(true);
830  redrawCCObjectAndChildren(item, true);
831  } else if (item->isA(CV_TYPES::LABEL_2D)) {
832  cc2DLabel* label = ccHObjectCaster::To2DLabel(item);
833  if (label) {
834  label->updateLabel();
835  }
836  } else if (item->isA(CV_TYPES::VIEWPORT_2D_LABEL)) {
837  cc2DViewportLabel* label =
839  if (label) {
840  label->updateLabel();
841  }
842  } else if (item->isKindOf(CV_TYPES::SENSOR)) {
843  ccSensor* sensor = ccHObjectCaster::ToSensor(item);
844  if (sensor) {
846  context.visible = sensor->isEnabled();
847  sensor->hideShowDrawings(context);
848  // for bbox
849  context.viewID = sensor->getViewId();
850  if (sensor->isSelected() && context.visible) {
851  // Check if Axes Grid is visible - if so, hide
852  // BoundingBox
853  bool shouldShowBB = true;
855  AxesGridProperties axesGridProps;
858  context.viewID, axesGridProps);
859  if (axesGridProps.visible) {
860  shouldShowBB = false;
861  }
862  }
863  if (shouldShowBB) {
864  sensor->showBB(context);
865  } else {
866  sensor->hideBB(context);
867  }
868  } else {
869  sensor->hideBB(context);
870  }
872  }
873  } else if (item->isKindOf(CV_TYPES::PRIMITIVE)) {
874  ccGenericPrimitive* prim =
876  if (prim) {
878  context.visible = prim->isEnabled();
879  prim->hideShowDrawings(context);
880  // for bbox
881  context.viewID = prim->getViewId();
882  if (prim->isSelected() && context.visible) {
883  // Check if Axes Grid is visible - if so, hide
884  // BoundingBox
885  bool shouldShowBB = true;
887  AxesGridProperties axesGridProps;
890  context.viewID, axesGridProps);
891  if (axesGridProps.visible) {
892  shouldShowBB = false;
893  }
894  }
895  if (shouldShowBB) {
896  prim->showBB(context);
897  } else {
898  prim->hideBB(context);
899  }
900  } else {
901  prim->hideBB(context);
902  }
904  }
905  } else {
906  // considering this item object may has not been added to
907  // rendering window
908  item->setForceRedrawRecursive(true);
910  redrawCCObjectAndChildren(item, false);
911  }
912  }
913 
914  return true;
915  }
916  }
917 
918  return false;
919 }
920 
921 QModelIndex ccDBRoot::index(int row,
922  int column,
923  const QModelIndex& parentIndex) const {
924  if (!hasIndex(row, column, parentIndex)) {
925  return QModelIndex();
926  }
927 
928  ccHObject* parent =
929  (parentIndex.isValid()
930  ? static_cast<ccHObject*>(parentIndex.internalPointer())
931  : m_treeRoot);
932  assert(parent);
933  if (!parent) {
934  return QModelIndex();
935  }
936 
937  ccHObject* child = parent->getChild(row);
938  return child ? createIndex(row, column, child) : QModelIndex();
939 }
940 
941 QModelIndex ccDBRoot::index(ccHObject* object) {
942  assert(object);
943 
944  if (object == m_treeRoot) {
945  return QModelIndex();
946  }
947 
948  ccHObject* parent = object->getParent();
949  if (!parent) {
950  // DGM: actually, it can happen (for instance if the entity is displayed
951  // in the local DB of a 3D view) CVLog::Error(QString("An error occurred
952  // while creating DB tree index: object '%1' has no
953  // parent").arg(object->getName()));
954  return QModelIndex();
955  }
956 
957  int pos = parent->getChildIndex(object);
958  assert(pos >= 0);
959 
960  return createIndex(pos, 0, object);
961 }
962 
963 QModelIndex ccDBRoot::parent(const QModelIndex& index) const {
964  if (!index.isValid()) {
965  return QModelIndex();
966  }
967 
968  ccHObject* childItem = static_cast<ccHObject*>(index.internalPointer());
969  if (!childItem) {
970  assert(false);
971  return QModelIndex();
972  }
973  ccHObject* parentItem = childItem->getParent();
974 
975  assert(parentItem);
976  if (!parentItem || parentItem == m_treeRoot) {
977  return QModelIndex();
978  }
979 
980  return createIndex(parentItem->getIndex(), 0, parentItem);
981 }
982 
983 int ccDBRoot::rowCount(const QModelIndex& parent) const {
984  ccHObject* parentItem = nullptr;
985  if (!parent.isValid())
986  parentItem = m_treeRoot;
987  else
988  parentItem = static_cast<ccHObject*>(parent.internalPointer());
989 
990  assert(parentItem);
991  return (parentItem ? parentItem->getChildrenNumber() : 0);
992 }
993 
994 int ccDBRoot::columnCount(const QModelIndex& parent) const {
995  Q_UNUSED(parent);
996  return 1;
997 }
998 
999 void ccDBRoot::changeSelection(const QItemSelection& selected,
1000  const QItemSelection& deselected) {
1001  // first unselect
1002  QModelIndexList deselectedItems = deselected.indexes();
1003  {
1004  for (int i = 0; i < deselectedItems.count(); ++i) {
1005  ccHObject* element = static_cast<ccHObject*>(
1006  deselectedItems.at(i).internalPointer());
1007  assert(element);
1008  if (element) {
1009  element->setSelected(false);
1010  }
1011  }
1012  }
1013 
1014  // then select
1015  QModelIndexList selectedItems = selected.indexes();
1016  {
1017  for (int i = 0; i < selectedItems.count(); ++i) {
1018  ccHObject* element = static_cast<ccHObject*>(
1019  selectedItems.at(i).internalPointer());
1020  assert(element);
1021  if (element) {
1022  element->setSelected(true);
1023  }
1024  }
1025  }
1026 
1028 
1030  MainWindow::TheInstance()->refreshAll(false, true);
1031 
1032  emit selectionChanged();
1033 }
1034 
1036  if (obj && obj->isSelected()) {
1037  QModelIndex objIndex = index(obj);
1038  if (objIndex.isValid()) {
1039  QItemSelectionModel* selectionModel =
1040  m_dbTreeWidget->selectionModel();
1041  assert(selectionModel);
1042  selectionModel->select(objIndex, QItemSelectionModel::Deselect);
1043  }
1044  }
1045 }
1046 
1048  QItemSelectionModel* selectionModel = m_dbTreeWidget->selectionModel();
1049  assert(selectionModel);
1050 
1051  selectionModel->clear();
1052 }
1053 
1055  bool forceAdditiveSelection /*=false*/) {
1056  bool additiveSelection =
1057  forceAdditiveSelection ||
1058  (QApplication::keyboardModifiers() & Qt::ControlModifier);
1059 
1060  QItemSelectionModel* selectionModel = m_dbTreeWidget->selectionModel();
1061  assert(selectionModel);
1062 
1063  // valid object? then we will try to select (or toggle) it
1064  if (obj) {
1065  QModelIndex selectedIndex = index(obj);
1066  if (selectedIndex.isValid()) {
1067  // if CTRL is pushed (or additive selection is forced)
1068  if (additiveSelection) {
1069  // default case: toggle current item selection state
1070  if (!obj->isSelected()) {
1071  QModelIndexList selectedIndexes =
1072  selectionModel->selectedIndexes();
1073  if (!selectedIndexes.empty()) {
1074  // special case: labels can only be merged with labels!
1075  if (obj->isA(CV_TYPES::LABEL_2D) !=
1076  static_cast<ccHObject*>(
1077  selectedIndexes[0].internalPointer())
1078  ->isA(CV_TYPES::LABEL_2D)) {
1080  tr("[Selection] Labels and other entities "
1081  "can't be mixed (release the CTRL key "
1082  "to start a new selection)"));
1083  return;
1084  }
1085  }
1086  }
1087  selectionModel->select(selectedIndex,
1088  QItemSelectionModel::Toggle);
1089  obj->setSelected(true);
1090  } else {
1091  if (selectionModel->isSelected(selectedIndex)) // nothing to do
1092  return;
1093  selectionModel->select(selectedIndex,
1094  QItemSelectionModel::ClearAndSelect);
1095  obj->setSelected(true);
1096  }
1097 
1098  // hack: auto-scroll to selected element
1099  if (obj->isSelected() && !additiveSelection)
1100  m_dbTreeWidget->scrollTo(selectedIndex);
1101  }
1102  }
1103  // otherwise we clear current selection (if CTRL is not pushed)
1104  else if (!additiveSelection) {
1105  selectionModel->clear();
1106  }
1107 }
1108 
1109 void ccDBRoot::selectEntities(std::unordered_set<int> entIDs) {
1110  bool ctrlPushed = (QApplication::keyboardModifiers() & Qt::ControlModifier);
1111 
1112  // convert input list of IDs to proper entities
1113  ccHObject::Container entities;
1114  {
1115  try {
1116  entities.reserve(entIDs.size());
1117  } catch (const std::bad_alloc&) {
1118  CVLog::Warning(tr("[ccDBRoot::selectEntities] Not enough memory"));
1119  return;
1120  }
1121 
1122  for (std::unordered_set<int>::const_iterator it = entIDs.begin();
1123  it != entIDs.end(); ++it) {
1124  ccHObject* obj = find(*it);
1125  if (obj) entities.push_back(obj);
1126  }
1127  }
1128 
1129  selectEntities(entities, ctrlPushed);
1130 }
1131 
1133  bool incremental /*=false*/) {
1134  // selection model
1135  QItemSelectionModel* selectionModel = m_dbTreeWidget->selectionModel();
1136  assert(selectionModel);
1137 
1138  // count the number of lables
1139  size_t labelCount = 0;
1140  {
1141  for (size_t i = 0; i < entities.size(); ++i) {
1142  ccHObject* ent = entities[i];
1143  if (!ent) {
1144  assert(false);
1145  continue;
1146  }
1147  if (ent->isA(CV_TYPES::LABEL_2D)) ++labelCount;
1148  }
1149  }
1150 
1151  // create new selection structure
1152  QItemSelection newSelection;
1153  {
1154  // shall we keep labels?
1155  bool keepLabels = false;
1156  {
1157  QModelIndexList formerSelectedIndexes =
1158  selectionModel->selectedIndexes();
1159  if (formerSelectedIndexes.isEmpty() || !incremental)
1160  keepLabels = (labelCount ==
1161  entities.size()); // yes if they are the only
1162  // selected entities
1163  else if (incremental)
1164  keepLabels =
1165  static_cast<ccHObject*>(
1166  formerSelectedIndexes[0].internalPointer())
1167  ->isA(CV_TYPES::LABEL_2D); // yes if previously
1168  // selected entities
1169  // were already
1170  // labels
1171  }
1172 
1173  for (size_t i = 0; i < entities.size(); ++i) {
1174  ccHObject* ent = entities[i];
1175  if (ent) {
1176  // filter input selection (can't keep both labels and standard
1177  // entities --> we can't mix them!)
1178  bool isLabel = ent->isA(CV_TYPES::LABEL_2D);
1179  if (isLabel == keepLabels &&
1180  (!incremental || !ent->isSelected())) {
1181  QModelIndex selectedIndex = index(ent);
1182  if (selectedIndex.isValid())
1183  newSelection.merge(
1184  QItemSelection(selectedIndex, selectedIndex),
1186  }
1187  }
1188  }
1189  }
1190 
1191  // default behavior: clear previous selection if CTRL is not pushed
1192  selectionModel->select(newSelection,
1193  incremental ? QItemSelectionModel::Select
1194  : QItemSelectionModel::ClearAndSelect);
1195 }
1196 
1197 ccHObject* ccDBRoot::find(int uniqueID) const {
1198  return m_treeRoot->find(uniqueID);
1199 }
1200 
1203 
1204  m_propertiesTreeWidget->setEnabled(true);
1206  // m_propertiesTreeWidget->setColumnWidth(1, m_propertiesTreeWidget->width()
1207  // - c_propViewLeftColumnWidth);
1208 }
1209 
1212  m_propertiesModel->clear();
1213  m_propertiesTreeWidget->setEnabled(false);
1214 }
1215 
1217  assert(m_ccPropDelegate);
1218  assert(m_propertiesTreeWidget);
1219  if (!m_propertiesTreeWidget->isEnabled() ||
1220  m_ccPropDelegate->getCurrentObject() != obj) {
1221  showPropertiesView(obj);
1222  }
1223 }
1224 
1226  assert(m_dbTreeWidget);
1227  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
1228  QModelIndexList selectedIndexes = qism->selectedIndexes();
1229  if (selectedIndexes.size() == 1) {
1231  static_cast<ccHObject*>(selectedIndexes[0].internalPointer()));
1232  } else {
1234  }
1235 }
1236 
1238  assert(object);
1239 
1240  QModelIndex idx = index(object);
1241 
1242  if (idx.isValid()) emit dataChanged(idx, idx);
1243 }
1244 
1245 void ccDBRoot::redrawCCObject(ccHObject* object, bool forceRedraw /* = true*/) {
1246  assert(object);
1247  object->redrawDisplay(forceRedraw);
1248 }
1249 
1251  bool forceRedraw /* = true*/) {
1252  assert(object);
1253  object->redrawDisplay(forceRedraw);
1254 }
1255 
1257  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
1258  QModelIndexList selectedIndexes = qism->selectedIndexes();
1259  int selCount = selectedIndexes.size();
1260 
1261  if (selCount == 0 || filter == CV_TYPES::OBJECT) return selCount;
1262 
1263  int realCount = 0;
1264  for (int i = 0; i < selCount; ++i) {
1265  ccHObject* object =
1266  static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
1267  if (object && object->isKindOf(filter)) ++realCount;
1268  }
1269 
1270  return realCount;
1271 }
1272 
1274  CV_CLASS_ENUM filter /*=CV_TYPES::OBJECT*/,
1275  dbTreeSelectionInfo* info /*=nullptr*/) {
1276  selectedEntities.clear();
1277 
1278  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
1279  QModelIndexList selectedIndexes = qism->selectedIndexes();
1280 
1281  try {
1282  int selCount = selectedIndexes.size();
1283  for (int i = 0; i < selCount; ++i) {
1284  ccHObject* object = static_cast<ccHObject*>(
1285  selectedIndexes[i].internalPointer());
1286  if (object && object->isKindOf(filter))
1287  selectedEntities.push_back(object);
1288  }
1289  } catch (const std::bad_alloc&) {
1290  // not enough memory!
1291  }
1292 
1293  if (info) {
1294  info->reset();
1295  info->selCount = selectedIndexes.size();
1296 
1297  for (size_t i = 0; i < info->selCount; ++i) {
1298  ccHObject* obj = selectedEntities[i];
1299 
1300  info->sfCount += obj->hasScalarFields() ? 1 : 0;
1301  info->colorCount += obj->hasColors() ? 1 : 0;
1302  info->normalsCount += obj->hasNormals() ? 1 : 0;
1303 
1304  if (obj->isA(CV_TYPES::HIERARCHY_OBJECT)) {
1305  info->groupCount++;
1306  } else if (obj->isKindOf(CV_TYPES::POINT_CLOUD)) {
1307  ccGenericPointCloud* genericCloud =
1309  info->cloudCount++;
1310  info->octreeCount +=
1311  genericCloud->getOctree() != nullptr ? 1 : 0;
1313  info->gridCound += qccCloud->gridCount();
1314  } else if (obj->isKindOf(CV_TYPES::MESH)) {
1315  info->meshCount++;
1316 
1317  if (obj->isKindOf(CV_TYPES::PLANE)) info->planeCount++;
1318  } else if (obj->isKindOf(CV_TYPES::POLY_LINE)) {
1319  info->polylineCount++;
1320 
1321  if (obj->isKindOf(CV_TYPES::CIRCLE)) {
1322  info->circleCount++;
1323  }
1324  } else if (obj->isKindOf(CV_TYPES::SENSOR)) {
1325  info->sensorCount++;
1326  if (obj->isKindOf(CV_TYPES::GBL_SENSOR)) info->gblSensorCount++;
1328  info->cameraSensorCount++;
1329  } else if (obj->isKindOf(CV_TYPES::POINT_KDTREE)) {
1330  info->kdTreeCount++;
1331  }
1332  }
1333  }
1334 
1335  return selectedEntities.size();
1336 }
1337 
1338 Qt::DropActions ccDBRoot::supportedDropActions() const {
1339  return Qt::MoveAction;
1340 }
1341 
1342 Qt::ItemFlags ccDBRoot::flags(const QModelIndex& index) const {
1343  if (!index.isValid()) return Qt::NoItemFlags;
1344 
1345  Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
1346 
1347  // common flags
1348  defaultFlags |= (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled |
1349  Qt::ItemIsSelectable | Qt::ItemIsEditable);
1350 
1351  // class type based filtering
1352  const ccHObject* item =
1353  static_cast<const ccHObject*>(index.internalPointer());
1354  assert(item);
1355  if (item && !item->isLocked()) // locked items cannot be drag-dropped
1356  {
1357  if (item->isA(CV_TYPES::HIERARCHY_OBJECT) ||
1358  (item->isKindOf(CV_TYPES::POINT_CLOUD) &&
1359  CanDetachCloud(item)) || // vertices can't be displaced
1360  (item->isKindOf(CV_TYPES::MESH) &&
1361  !item->isA(CV_TYPES::SUB_MESH)) || // a sub-mesh can't leave its
1362  // parent mesh
1363  item->isKindOf(CV_TYPES::IMAGE) ||
1364  item->isKindOf(CV_TYPES::LABEL_2D) ||
1366  item->isKindOf(CV_TYPES::PRIMITIVE) ||
1367  item->isKindOf(CV_TYPES::FACET) ||
1369 
1370  {
1371  defaultFlags |= (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
1372  } else if (item->isKindOf(CV_TYPES::POLY_LINE)) {
1373  const ccPolyline* poly = static_cast<const ccPolyline*>(item);
1374  // we can only displace a polyline if it is not dependent on it's
1375  // father!
1376  const ccHObject* polyVertices =
1377  dynamic_cast<const ccHObject*>(poly->getAssociatedCloud());
1378  if (polyVertices != poly->getParent()) {
1379  defaultFlags |= (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
1380  }
1381  } else if (item->isKindOf(CV_TYPES::VIEWPORT_2D_OBJECT)) {
1382  defaultFlags |= Qt::ItemIsDragEnabled;
1383  }
1384  }
1385 
1386  return defaultFlags;
1387 }
1388 
1389 QMap<int, QVariant> ccDBRoot::itemData(const QModelIndex& index) const {
1390  QMap<int, QVariant> map = QAbstractItemModel::itemData(index);
1391 
1392  if (index.isValid()) {
1393  const ccHObject* object =
1394  static_cast<const ccHObject*>(index.internalPointer());
1395  if (object) map.insert(Qt::UserRole, QVariant(object->getUniqueID()));
1396  }
1397 
1398  return map;
1399 }
1400 
1401 bool ccDBRoot::dropMimeData(const QMimeData* data,
1402  Qt::DropAction action,
1403  int destRow,
1404  int destColumn,
1405  const QModelIndex& destParent) {
1406  Q_UNUSED(destColumn);
1407  if (action != Qt::MoveAction) {
1408  return false;
1409  }
1410 
1411  // default mime type for QAbstractItemModel items)
1412  if (!data->hasFormat("application/x-qabstractitemmodeldatalist")) {
1413  return false;
1414  }
1415 
1416  // new parent (can't be a leaf object!)
1417  ccHObject* newParent =
1418  destParent.isValid()
1419  ? static_cast<ccHObject*>(destParent.internalPointer())
1420  : m_treeRoot;
1421  if (newParent && newParent->isLeaf()) {
1422  return false;
1423  }
1424 
1425  // decode data
1426  QByteArray encoded = data->data("application/x-qabstractitemmodeldatalist");
1427  QDataStream stream(&encoded, QIODevice::ReadOnly);
1428  while (!stream.atEnd()) {
1429  // decode current item index data (row, col, data 'roles' map)
1430  int srcRow, srcCol;
1431  QMap<int, QVariant> roleDataMap;
1432  stream >> srcRow >> srcCol >> roleDataMap;
1433  if (!roleDataMap.contains(Qt::UserRole)) continue;
1434 
1435  // selected item
1436  int uniqueID = roleDataMap.value(Qt::UserRole).toInt();
1437  ccHObject* item = m_treeRoot->find(uniqueID);
1438  if (!item) continue;
1439  // CVLog::Print(QString("[Drag & Drop] Source:
1440  // %1").arg(item->getName()));
1441 
1442  // old parent
1443  ccHObject* oldParent = item->getParent();
1444  // CVLog::Print(QString("[Drag & Drop] Parent: %1").arg(oldParent ?
1445  // oldParent->getName() : "none")));
1446 
1447  // let's check if we can actually move the entity
1448  if (oldParent) {
1449  if (item->isKindOf(CV_TYPES::POINT_CLOUD)) {
1450  // point cloud == mesh vertices?
1451  if (oldParent->isKindOf(CV_TYPES::MESH) &&
1453  ->getAssociatedCloud() == item) {
1454  if (oldParent != newParent) {
1455  CVLog::Error(
1456  tr("Vertices can't leave their parent mesh"));
1457  return false;
1458  }
1459  }
1460  } else if (item->isKindOf(CV_TYPES::MESH)) {
1461  // a sub-mesh can't leave its parent mesh
1462  if (item->isA(CV_TYPES::SUB_MESH)) {
1463  assert(false);
1464  CVLog::Error(tr("Sub-meshes can't leave their mesh group"));
1465  return false;
1466  }
1467  // a mesh can't leave its associated cloud
1468  else if (oldParent->isKindOf(CV_TYPES::POINT_CLOUD) &&
1470  ->getAssociatedCloud() == oldParent) {
1471  if (oldParent != newParent) {
1472  CVLog::Error(
1473  tr("Meshes can't leave their associated cloud "
1474  "(vertices set)"));
1475  return false;
1476  }
1477  }
1478  } else if (/*item->isKindOf(CV_TYPES::PRIMITIVE) || */ item
1479  ->isKindOf(CV_TYPES::IMAGE)) {
1480  if (oldParent != newParent) {
1481  CVLog::Error(
1482  tr("This kind of entity can't leave their parent"));
1483  return false;
1484  }
1485  } else if (oldParent != newParent) {
1486  // a label or a group of labels can't be moved to another cloud!
1487  // ccHObject::Container labels;
1488  // if (item->isA(CV_TYPES::LABEL_2D))
1489  // labels.push_back(item);
1490  // else
1491  // item->filterChildren(labels, true, CV_TYPES::LABEL_2D);
1492 
1494  // for (ccHObject::Container::const_iterator it =
1495  // labels.begin(); it != labels.end(); ++it)
1496  //{
1497  // if ((*it)->isA(CV_TYPES::LABEL_2D)) //Warning:
1498  // cc2DViewportLabel is also a kind of 'CV_TYPES::LABEL_2D'!
1499  // {
1500  // cc2DLabel* label = static_cast<cc2DLabel*>(*it);
1501  // bool canMove = false;
1502  // for (unsigned j = 0; j < label->size(); ++j)
1503  // {
1504  // assert(label->getPoint(j).cloud);
1505  // //3 options to allow moving a label:
1506  // if
1507  //(item->isAncestorOf(label->getPoint(j).cloud) //label's cloud
1508  // is inside sub-tree
1509  // || newParent ==
1510  // label->getPoint(j).cloud //destination is label's cloud
1511  // ||
1512  // label->getPoint(j).cloud->isAncestorOf(newParent))
1514  // {
1515  // canMove = true;
1516  // break;
1517  // }
1518  // }
1519 
1520  // if (!canMove)
1521  // {
1522  // CVLog::Error("Labels (or group of) can't
1523  // leave their parent"); return false;
1524  // }
1525  // }
1526  //}
1527  }
1528  }
1529 
1530  // special case: moving an item inside the same 'parent'
1531  if (oldParent && newParent == oldParent) {
1532  int oldRow = oldParent->getChildIndex(item);
1533  if (destRow < 0) {
1534  assert(oldParent->getChildrenNumber() != 0);
1535  destRow = static_cast<int>(oldParent->getChildrenNumber()) - 1;
1536  } else if (oldRow < destRow) {
1537  assert(destRow > 0);
1538  --destRow;
1539  } else if (oldRow == destRow) {
1540  return false; // nothing to do
1541  }
1542  }
1543 
1544  // remove link with old parent (only CHILD/PARENT related flags!)
1545  int itemDependencyFlags = item->getDependencyFlagsWith(
1546  oldParent); // works even with nullptr
1547  int fatherDependencyFlags =
1548  oldParent ? oldParent->getDependencyFlagsWith(item) : 0;
1549  if (oldParent) {
1550  oldParent->removeDependencyFlag(item,
1552  item->removeDependencyFlag(oldParent,
1554  }
1555 
1556  // remove item from current position
1557  removeElement(item);
1558 
1559  // sets new parent
1560  assert(newParent);
1561  newParent->addChild(
1562  item, fatherDependencyFlags & ccHObject::DP_PARENT_OF_OTHER,
1563  destRow);
1564  item->addDependency(
1565  newParent, itemDependencyFlags & ccHObject::DP_PARENT_OF_OTHER);
1566  // restore other flags on old parent (as all flags have been removed
1567  // when calling removeElement!)
1568  if (oldParent) {
1569  oldParent->addDependency(
1570  item,
1571  fatherDependencyFlags & (~ccHObject::DP_PARENT_OF_OTHER));
1572  item->addDependency(
1573  oldParent,
1574  itemDependencyFlags & (~ccHObject::DP_PARENT_OF_OTHER));
1575  }
1576 
1577  // add item back
1578  addElement(item, false);
1579  }
1580 
1581  // MainWindow::RefreshAllGLWindow(false);
1582  MainWindow::TheInstance()->refreshAll(false, false);
1583  return true;
1584 }
1585 
1587 
1589 
1591  // not initialized?
1592  if (m_contextMenuPos.x() < 0 || m_contextMenuPos.y() < 0) return;
1593 
1594  QModelIndex clickIndex = m_dbTreeWidget->indexAt(m_contextMenuPos);
1595  if (!clickIndex.isValid()) return;
1596  ccHObject* item = static_cast<ccHObject*>(clickIndex.internalPointer());
1597  assert(item);
1598 
1599  if (!item || item->getChildrenNumber() == 0) return;
1600 
1601  // we recursively expand sub-branches
1602  ccHObject::Container toExpand;
1603  try {
1604  toExpand.push_back(item);
1605  while (!toExpand.empty()) {
1606  item = toExpand.back();
1607  toExpand.pop_back();
1608 
1609  QModelIndex itemIndex = index(item);
1610  if (itemIndex.isValid()) {
1611  if (expand)
1612  m_dbTreeWidget->expand(itemIndex);
1613  else
1614  m_dbTreeWidget->collapse(itemIndex);
1615  }
1616 
1617  assert(item->getChildrenNumber() != 0);
1618  for (unsigned i = 0; i < item->getChildrenNumber(); ++i) {
1619  if (item->getChild(i)->getChildrenNumber() != 0)
1620  toExpand.push_back(item->getChild(i));
1621  }
1622  }
1623  } catch (const std::bad_alloc&) {
1624  // not enough memory!
1625  }
1626 }
1627 
1629  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
1630  QModelIndexList selectedIndexes = qism->selectedIndexes();
1631  int selCount = selectedIndexes.size();
1632  if (selCount == 0) return;
1633 
1634  ccHObject* obj =
1635  static_cast<ccHObject*>(selectedIndexes[0].internalPointer());
1636  if (!obj) return;
1637 
1638  // plane normal
1639  CCVector3d planeNormal;
1640  CCVector3d planeVertDir;
1641  CCVector3 center;
1642 
1643  if (obj->isA(CV_TYPES::LABEL_2D)) // 2D label with 3 points?
1644  {
1645  cc2DLabel* label = static_cast<cc2DLabel*>(obj);
1646  // work only with labels with 3 points!
1647  if (label->size() == 3) {
1648  const cc2DLabel::PickedPoint& A = label->getPickedPoint(0);
1649  const CCVector3* _A = A.cloud->getPoint(A.index);
1650  const cc2DLabel::PickedPoint& B = label->getPickedPoint(1);
1651  const CCVector3* _B = B.cloud->getPoint(B.index);
1652  const cc2DLabel::PickedPoint& C = label->getPickedPoint(2);
1653  const CCVector3* _C = C.cloud->getPoint(C.index);
1654  CCVector3 N = (*_B - *_A).cross(*_C - *_A);
1655  planeNormal = CCVector3d::fromArray(N.u);
1656  // planeVertDir = /*(*_B-*_A)*/win->getCurrentUpDir();
1657  center = (*_A + *_B + *_C) / 3;
1658  } else {
1659  assert(false);
1660  return;
1661  }
1662  } else if (obj->isA(CV_TYPES::PLANE)) // plane
1663  {
1664  ccPlane* plane = static_cast<ccPlane*>(obj);
1665  // 3rd column = plane normal!
1666  planeNormal = CCVector3d::fromArray(plane->getNormal().u);
1667  planeVertDir = CCVector3d::fromArray(
1668  plane->getTransformation().getColumnAsVec3D(1).u);
1669  center = plane->getOwnBB().getCenter();
1670  } else if (obj->isA(CV_TYPES::FACET)) // facet
1671  {
1672  ccFacet* facet = static_cast<ccFacet*>(obj);
1673  planeNormal = CCVector3d::fromArray(facet->getNormal().u);
1674  CCVector3d planeHorizDir(0, 1, 0);
1675  cloudViewer::CCMiscTools::ComputeBaseVectors(planeNormal, planeHorizDir,
1676  planeVertDir);
1677  center = facet->getBB_recursive(false, false).getCenter();
1678  } else {
1679  assert(false);
1680  return;
1681  }
1682 
1683  // we can now make the camera look in the direction of the normal
1684  if (!reverse) planeNormal *= -1;
1685 
1686  // output the transformation matrix that would make this normal points
1687  // towards +Z
1688  {
1689  ccGLMatrixd transMat;
1690  transMat.setTranslation(-center);
1691 
1692  CVLog::Print(tr("[Align camera] Corresponding view matrix:"));
1693  CVLog::Print(
1694  tr("[Orientation] You can copy this matrix values (CTRL+C) and "
1695  "paste them in the 'Apply transformation tool' dialog"));
1696  }
1697 }
1698 
1700  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
1701  QModelIndexList selectedIndexes = qism->selectedIndexes();
1702  int selCount = selectedIndexes.size();
1703  if (selCount == 0) return;
1704 
1705  struct GlobalInfo {
1706  // properties
1707  unsigned pointCount;
1708  unsigned triangleCount;
1709  unsigned colorCount;
1710  unsigned normalCount;
1711  unsigned materialCount;
1712  unsigned scalarFieldCount;
1713 
1714  // entities
1715  unsigned cloudCount;
1716  unsigned meshCount;
1717  unsigned octreeCount;
1718  unsigned imageCount;
1719  unsigned sensorCount;
1720  unsigned labelCount;
1721  } info;
1722 
1723  memset(&info, 0, sizeof(GlobalInfo));
1724 
1725  // init the list of entities to process
1726  ccHObject::Container toProcess;
1727  try {
1728  toProcess.resize(selCount);
1729  } catch (const std::bad_alloc&) {
1730  CVLog::Error(tr("Not engough memory"));
1731  return;
1732  }
1733 
1734  for (int i = 0; i < selCount; ++i) {
1735  toProcess[i] =
1736  static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
1737  }
1738 
1739  ccHObject::Container alreadyProcessed;
1740  while (!toProcess.empty()) {
1741  ccHObject* ent = toProcess.back();
1742  toProcess.pop_back();
1743 
1744  // we don't process entities twice!
1745  if (std::find(alreadyProcessed.begin(), alreadyProcessed.end(), ent) !=
1746  alreadyProcessed.end()) {
1747  continue;
1748  }
1749 
1750  // gather information from current entity
1751  if (ent->isA(CV_TYPES::POINT_CLOUD)) {
1752  ccPointCloud* cloud = static_cast<ccPointCloud*>(ent);
1753  info.cloudCount++;
1754 
1755  unsigned cloudSize = cloud->size();
1756  info.pointCount += cloudSize;
1757  info.colorCount += (cloud->hasColors() ? cloudSize : 0);
1758  info.normalCount += (cloud->hasNormals() ? cloudSize : 0);
1759  info.scalarFieldCount += cloud->getNumberOfScalarFields();
1760  } else if (ent->isKindOf(CV_TYPES::MESH)) {
1761  ccMesh* mesh = static_cast<ccMesh*>(ent);
1762 
1763  info.meshCount++;
1764  unsigned meshSize = mesh->size();
1765  info.triangleCount += meshSize;
1766  info.normalCount += (mesh->hasTriNormals() ? meshSize : 0);
1767  info.materialCount += 0;
1768  } else if (ent->isKindOf(CV_TYPES::LABEL_2D)) {
1769  info.labelCount++;
1770  } else if (ent->isKindOf(CV_TYPES::SENSOR)) {
1771  info.sensorCount++;
1772  } else if (ent->isKindOf(CV_TYPES::POINT_OCTREE)) {
1773  info.octreeCount++;
1774  } else if (ent->isKindOf(CV_TYPES::IMAGE)) {
1775  info.imageCount++;
1776  }
1777 
1778  // we can add its children to the 'toProcess' list and itself to the
1779  // 'processed' list
1780  try {
1781  for (unsigned i = 0; i < ent->getChildrenNumber(); ++i) {
1782  toProcess.push_back(ent->getChild(i));
1783  }
1784  alreadyProcessed.push_back(ent);
1785  } catch (const std::bad_alloc&) {
1786  CVLog::Error(tr("Not engough memory"));
1787  return;
1788  }
1789  }
1790 
1791  // output information
1792  {
1793  QStringList infoStr;
1794 
1795  const QString separator("--------------------------");
1796 
1797  infoStr << tr("Point(s):\t\t%1").arg(info.pointCount);
1798  infoStr << tr("Triangle(s):\t\t%1").arg(info.triangleCount);
1799 
1800  infoStr << separator;
1801  if (info.colorCount)
1802  infoStr << tr("Color(s):\t\t%1").arg(info.colorCount);
1803  if (info.normalCount)
1804  infoStr << tr("Normal(s):\t\t%1").arg(info.normalCount);
1805  if (info.scalarFieldCount)
1806  infoStr << tr("Scalar field(s):\t\t%1").arg(info.scalarFieldCount);
1807  if (info.materialCount)
1808  infoStr << tr("Material(s):\t\t%1").arg(info.materialCount);
1809 
1810  infoStr << separator;
1811  infoStr << tr("Cloud(s):\t\t%1").arg(info.cloudCount);
1812  infoStr << tr("Mesh(es):\t\t%1").arg(info.meshCount);
1813  if (info.octreeCount)
1814  infoStr << tr("Octree(s):\t\t%1").arg(info.octreeCount);
1815  if (info.imageCount)
1816  infoStr << tr("Image(s):\t\t%1").arg(info.imageCount);
1817  if (info.labelCount)
1818  infoStr << tr("Label(s):\t\t%1").arg(info.labelCount);
1819  if (info.sensorCount)
1820  infoStr << tr("Sensor(s):\t\t%1").arg(info.sensorCount);
1821 
1822  // display info box
1823  QMessageBox::information(MainWindow::TheInstance(), tr("Information"),
1824  infoStr.join("\n"));
1825  }
1826 }
1827 
1829 
1831 
1834 }
1835 
1837  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
1838  QModelIndexList selectedIndexes = qism->selectedIndexes();
1839  int selCount = selectedIndexes.size();
1840  if (selCount == 0) return;
1841 
1842  for (int i = 0; i < selCount; ++i) {
1843  ccHObject* item =
1844  static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
1845  unsigned childCount = (item ? item->getChildrenNumber() : 0);
1846  if (childCount > 1) {
1847  // remove all children from DB tree
1848  beginRemoveRows(selectedIndexes[i], 0, childCount - 1);
1849 
1850  // row removal operation (end)
1851  endRemoveRows();
1852 
1853  // sort
1854  for (unsigned k = 0; k < childCount - 1; ++k) {
1855  unsigned firstChildIndex = k;
1856  ccHObject* firstChild = item->getChild(k);
1857  QString firstChildName = firstChild->getName().toUpper();
1858 
1859  for (unsigned j = k + 1; j < childCount; ++j) {
1860  bool swap = false;
1861  QString currentName =
1862  item->getChild(j)->getName().toUpper();
1863  switch (sortRule) {
1864  case SORT_A2Z:
1865  swap = (firstChildName.compare(currentName) > 0);
1866  break;
1867  case SORT_Z2A:
1868  swap = (firstChildName.compare(currentName) < 0);
1869  break;
1870  case SORT_BY_TYPE:
1871  if (firstChild->getClassID() ==
1872  item->getChild(j)->getClassID())
1873  swap = (firstChildName.compare(currentName) >
1874  0); // A2Z in second choice
1875  else
1876  swap = (firstChild->getClassID() >
1877  item->getChild(j)->getClassID());
1878  break;
1879  }
1880 
1881  if (swap) {
1882  firstChildIndex = j;
1883  firstChildName = currentName;
1884  }
1885  }
1886 
1887  if (k != firstChildIndex)
1888  item->swapChildren(k, firstChildIndex);
1889  }
1890 
1891  // add children back
1892  beginInsertRows(selectedIndexes[i], 0, childCount - 1);
1893 
1894  // row insertion operation (end)
1895  endInsertRows();
1896  }
1897  }
1898 }
1899 
1902  scDlg.addType(tr("Point cloud"), CV_TYPES::POINT_CLOUD);
1903  scDlg.addType(tr("Poly-line"), CV_TYPES::POLY_LINE);
1904  scDlg.addType(tr("Mesh"), CV_TYPES::MESH);
1905  scDlg.addType(tr(" Sub-mesh"), CV_TYPES::SUB_MESH);
1906  scDlg.addType(tr(" Primitive"), CV_TYPES::PRIMITIVE);
1907  scDlg.addType(tr(" Plane"), CV_TYPES::PLANE);
1908  scDlg.addType(tr(" Sphere"), CV_TYPES::SPHERE);
1909  scDlg.addType(tr(" Torus"), CV_TYPES::TORUS);
1910  scDlg.addType(tr(" Cylinder"), CV_TYPES::CYLINDER);
1911  scDlg.addType(tr(" Cone"), CV_TYPES::CONE);
1912  scDlg.addType(tr(" Box"), CV_TYPES::BOX);
1913  scDlg.addType(tr(" Dish"), CV_TYPES::DISH);
1914  scDlg.addType(tr(" Disc"), CV_TYPES::DISC);
1915  scDlg.addType(tr(" Extrusion"), CV_TYPES::EXTRU);
1916  scDlg.addType(tr("Sensor"), CV_TYPES::SENSOR);
1917  scDlg.addType(tr(" GBL/TLS sensor"), CV_TYPES::GBL_SENSOR);
1918  scDlg.addType(tr(" Camera sensor"), CV_TYPES::CAMERA_SENSOR);
1919  scDlg.addType(tr("Image"), CV_TYPES::IMAGE);
1920  scDlg.addType(tr("Facet"), CV_TYPES::FACET);
1921  scDlg.addType(tr("Label"), CV_TYPES::LABEL_2D);
1922  scDlg.addType(tr("Area label"), CV_TYPES::VIEWPORT_2D_LABEL);
1923  scDlg.addType(tr("Octree"), CV_TYPES::POINT_OCTREE);
1924  scDlg.addType(tr("Kd-tree"), CV_TYPES::POINT_KDTREE);
1925  scDlg.addType(tr("Viewport"), CV_TYPES::VIEWPORT_2D_OBJECT);
1926  scDlg.addType(tr("Custom Types"), CV_TYPES::CUSTOM_H_OBJECT);
1927 
1928  if (!scDlg.exec()) return;
1929 
1930  // for type checking
1931  CV_CLASS_ENUM type = CV_TYPES::OBJECT; // all objects are matched by def
1932  bool exclusive = false;
1933 
1934  if (scDlg.getTypeIsUsed()) // we are using type checking
1935  {
1936  type = scDlg.getSelectedType();
1937 
1938  // some types are exclusive, some are generic, and some can be both
1939  //(e.g. Meshes)
1940  //
1941  // For generic-only types the match type gets overridden and forced to
1942  // false because exclusive match makes no sense!
1943  switch (type) {
1944  case CV_TYPES::HIERARCHY_OBJECT: // returned if no type is selected
1945  // (i.e. all objects are
1946  // selected!)
1947  case CV_TYPES::PRIMITIVE:
1948  case CV_TYPES::SENSOR:
1949  case CV_TYPES::IMAGE:
1950  exclusive = false;
1951  break;
1952  default:
1953  exclusive = scDlg.getStrictMatchState();
1954  break;
1955  }
1956  }
1957 
1958  // for name matching - def values
1959  bool regex = false;
1960  QString name; // an empty string by default
1961 
1962  if (scDlg.getNameMatchIsUsed()) {
1963  regex = scDlg.getNameIsRegex();
1964  name = scDlg.getSelectedName();
1965  }
1966 
1967  selectChildrenByTypeAndName(type, exclusive, name, regex);
1968 }
1969 
1970 /* name is optional, if passed it is used to restrict the selection by type */
1972  bool typeIsExclusive /*=true*/,
1973  QString name /*=QString()*/,
1974  bool nameIsRegex /*= false*/) {
1975  // not initialized?
1976  if (m_contextMenuPos.x() < 0 || m_contextMenuPos.y() < 0) return;
1977 
1978  QModelIndex clickIndex = m_dbTreeWidget->indexAt(m_contextMenuPos);
1979  if (!clickIndex.isValid()) return;
1980  ccHObject* item = static_cast<ccHObject*>(clickIndex.internalPointer());
1981  assert(item);
1982 
1983  if (!item || item->getChildrenNumber() == 0) return;
1984 
1985  ccHObject::Container filteredByType;
1986  item->filterChildren(filteredByType, true, type, typeIsExclusive);
1987 
1988  // The case of an empty filteredByType is handled implicitly, to make
1989  // the ctrlPushed behavior below more consistent (i.e. when no object
1990  // is found and Control was NOT pressed the selection will still be
1991  // cleared).
1992  ccHObject::Container toSelect;
1993  try {
1994  if (name.isEmpty()) {
1995  toSelect = filteredByType;
1996  } else {
1997  for (size_t i = 0; i < filteredByType.size(); ++i) {
1998  ccHObject* child = filteredByType[i];
1999 
2000  if (nameIsRegex) // regex matching
2001  {
2002  QRegularExpression re(name);
2003  QRegularExpressionMatch match = re.match(child->getName());
2004  bool hasMatch = match.hasMatch(); // true
2005  if (hasMatch) toSelect.push_back(child);
2006 
2007  }
2008 
2009  else if (child->getName().compare(name) ==
2010  0) // simple comparison
2011  toSelect.push_back(child);
2012  }
2013  }
2014  } catch (const std::bad_alloc&) {
2015  CVLog::Warning(tr("[selectChildrenByTypeAndName] Not enough memory"));
2016  return;
2017  }
2018 
2019  bool ctrlPushed = (QApplication::keyboardModifiers() & Qt::ControlModifier);
2020  selectEntities(toSelect, ctrlPushed);
2021 }
2022 
2024  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
2025  QModelIndexList selectedIndexes = qism->selectedIndexes();
2026  int selCount = selectedIndexes.size();
2027  if (selCount == 0) return;
2028 
2029  // hide properties view
2031 
2032  for (int i = 0; i < selCount; ++i) {
2033  ccHObject* item =
2034  static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
2035  if (!item) {
2036  assert(false);
2037  continue;
2038  }
2039  switch (prop) {
2040  case TG_ENABLE: // enable state
2041  item->setEnabled(!item->isEnabled());
2042  break;
2043  case TG_VISIBLE: // visibility
2044  item->toggleVisibility();
2045  item->setForceRedrawRecursive(true);
2046  break;
2047  case TG_COLOR: // color
2048  item->toggleColors();
2049  break;
2050  case TG_NORMAL: // normal
2051  item->toggleNormals();
2052  break;
2053  case TG_SF: // SF
2054  item->toggleSF();
2055  break;
2056  case TG_3D_NAME: // 3D name
2057  item->toggleShowName();
2058  break;
2059  }
2060  }
2061 
2062  // we restablish properties view
2064 
2065  // MainWindow::RefreshAllGLWindow(false);
2066  if (TG_ENABLE == prop || TG_VISIBLE == prop || TG_3D_NAME == prop) {
2067  // draw 3D and no need to forceredraw
2068  MainWindow::TheInstance()->refreshAll(true, false);
2069  } else {
2071  }
2072 }
2073 
2075  // not initialized?
2076  if (m_contextMenuPos.x() < 0 || m_contextMenuPos.y() < 0) return;
2077 
2078  QModelIndex index = m_dbTreeWidget->indexAt(m_contextMenuPos);
2079  ccHObject* newGroup = new ccHObject(tr("Group"));
2080  if (index.isValid()) {
2081  ccHObject* parent = static_cast<ccHObject*>(index.internalPointer());
2082  if (parent) parent->addChild(newGroup);
2083  }
2084 
2085  addElement(newGroup);
2086 }
2087 
2089  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
2090  QModelIndexList selectedIndexes = qism->selectedIndexes();
2091  int selCount = selectedIndexes.size();
2092  if (selCount == 0) return;
2093 
2094  for (int i = 0; i < selCount; ++i) {
2095  ccHObject* item =
2096  static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
2097  if (item && item->isA(CV_TYPES::GBL_SENSOR)) {
2098  static_cast<ccGBLSensor*>(item)->applyViewport();
2099  }
2100  }
2101 
2102  // MainWindow::RefreshAllGLWindow(false);
2104 }
2105 
2107  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
2108  QModelIndexList selectedIndexes = qism->selectedIndexes();
2109  int selCount = selectedIndexes.size();
2110  if (selCount == 0) {
2111  return;
2112  }
2113 
2114  ccHObject* obj =
2115  static_cast<ccHObject*>(selectedIndexes[0].internalPointer());
2116  cc2DLabel* label = ccHObjectCaster::To2DLabel(obj);
2117  if (!label || label->size() != 1) {
2118  return;
2119  }
2120 
2121  const cc2DLabel::PickedPoint& P = label->getPickedPoint(0);
2122  if (!P.cloud) {
2123  assert(false);
2124  return;
2125  }
2126 
2127  if (!P.cloud->isA(CV_TYPES::POINT_CLOUD) || !P.cloud->hasScalarFields()) {
2128  return;
2129  }
2130 
2131  ccPointCloud* pc = static_cast<ccPointCloud*>(P.cloud);
2133  if (!sf) {
2134  CVLog::Warning(tr("[editLabelScalarValue] No active scalar field"));
2135  return;
2136  }
2137 
2138  ScalarType s = sf->getValue(P.index);
2139 
2140  bool ok = false;
2141  double newValue = QInputDialog::getDouble(
2142  MainWindow::TheInstance(), tr("Edit scalar value"),
2143  QString("%1 (%2) =").arg(sf->getName()).arg(P.index), s,
2144  -2147483647, 2147483647, 6, &ok);
2145  if (!ok) {
2146  // process cancelled by the user
2147  return;
2148  }
2149 
2150  ScalarType newS = static_cast<ScalarType>(newValue);
2151  if (s != newS) {
2152  // update the value and update the display
2153  sf->setValue(P.index, newS);
2154  sf->computeMinAndMax();
2155  pc->redrawDisplay();
2156  }
2157 }
2158 
2160  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
2161  QModelIndexList selectedIndexes = qism->selectedIndexes();
2162  int selCount = selectedIndexes.size();
2163  if (selCount == 0) {
2164  return;
2165  }
2166 
2167  // find the images in the current selection
2168  std::set<ccImage*> images;
2169  try {
2170  for (int i = 0; i < selCount; ++i) {
2171  ccHObject* item = static_cast<ccHObject*>(
2172  selectedIndexes[i].internalPointer());
2173  if (item->isKindOf(CV_TYPES::IMAGE)) {
2174  images.insert(ccHObjectCaster::ToImage(item));
2175  } else {
2176  // look for the children
2177  ccHObject::Container filteredChildren;
2178  if (item->filterChildren(filteredChildren, true,
2179  CV_TYPES::IMAGE) != 0) {
2180  for (ccHObject* obj : filteredChildren) {
2181  images.insert(ccHObjectCaster::ToImage(obj));
2182  }
2183  }
2184  }
2185  }
2186  } catch (const std::bad_alloc&) {
2187  CVLog::Error(tr("Not enough memory"));
2188  return;
2189  }
2190 
2191  if (images.empty()) {
2192  CVLog::Warning(tr("No image in selection"));
2193  return;
2194  }
2195 
2196  // get default export folder from persistent settings
2197  QString saveDirectoryName;
2198  {
2199  QSettings settings;
2200  settings.beginGroup("SaveFile");
2201  QString currentPath =
2202  settings.value("CurrentPath", QDir::homePath()).toString();
2203 
2204  saveDirectoryName = QFileDialog::getExistingDirectory(
2205  MainWindow::TheInstance(), tr("Choose destination directory"),
2206  currentPath,
2207  QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
2208 
2209  if (saveDirectoryName.isEmpty()) {
2210  // process cancelled by the user
2211  return;
2212  }
2213 
2214  // save last saving location
2215  settings.setValue("CurrentPath", saveDirectoryName);
2216  settings.endGroup();
2217  }
2218 
2219  QDir saveDirectory(saveDirectoryName);
2220 
2221  if (false == saveDirectory.exists()) {
2222  CVLog::Error(tr("Directory doesn't exist"));
2223  return;
2224  }
2225 
2227  pDlg.setRange(0, static_cast<int>(images.size()));
2228  pDlg.setWindowTitle(tr("Export images"));
2229  pDlg.start();
2230  pDlg.show();
2231  QCoreApplication::processEvents();
2232 
2233  // Save the images to the provided directory
2234  int overwriteImagesAnswer = QMessageBox::StandardButton::Default;
2235  QMap<QString, size_t> duplicateNameCounter;
2236  int imageCounter = 0;
2237  for (const ccImage* image : images) {
2238  QString baseName = image->getName();
2239  {
2240  // make sure the name is unique
2241  if (duplicateNameCounter.contains(baseName)) {
2242  baseName +=
2243  QString("_%1").arg(duplicateNameCounter[baseName] + 1);
2244  }
2245  duplicateNameCounter[baseName] += 1;
2246  }
2247 
2248  baseName += QStringLiteral(".png");
2249  QString filename = saveDirectory.absoluteFilePath(baseName);
2250 
2251  bool skipImage = false;
2252  if (QFile(filename).exists()) {
2253  if (overwriteImagesAnswer == QMessageBox::StandardButton::Default) {
2254  // first time: ask the question to the user
2255  overwriteImagesAnswer = QMessageBox::question(
2256  MainWindow::TheInstance(), tr("Overwriting files"),
2257  tr("Overwrite existing files?"),
2258  QMessageBox::StandardButton::YesToAll,
2259  QMessageBox::StandardButton::NoToAll);
2260  }
2261 
2262  if (overwriteImagesAnswer == QMessageBox::StandardButton::NoToAll) {
2263  CVLog::Warning(tr("Image %1 has not been saved so as to not "
2264  "overwrite an existing file")
2265  .arg(baseName));
2266  skipImage = true;
2267  }
2268  }
2269 
2270  if (!skipImage) {
2271  image->data().save(filename);
2272  }
2273 
2274  if (pDlg.isCancelRequested()) {
2275  break;
2276  }
2277  pDlg.setValue(++imageCounter);
2278  }
2279 
2280  pDlg.stop();
2281  CVLog::Print(tr("[Export images] %1 image(s) exported").arg(imageCounter));
2282 }
2283 
2284 void ccDBRoot::showContextMenu(const QPoint& menuPos) {
2285  m_contextMenuPos = menuPos;
2286 
2287  // build custom context menu
2288  QMenu menu;
2289 
2290  QModelIndex index = m_dbTreeWidget->indexAt(menuPos);
2291  if (index.isValid()) {
2292  QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
2293 
2294  // selected items?
2295  QModelIndexList selectedIndexes = qism->selectedIndexes();
2296  int selCount = selectedIndexes.size();
2297  if (selCount) {
2298  bool toggleVisibility = false;
2299  bool toggleOtherProperties = false;
2300  bool toggleMaterials = false;
2301  bool hasMoreThanOneChild = false;
2302  bool hasExactlyOnePlanarEntity = false;
2303  bool leafObject = false;
2304  bool hasExacltyOneGBLSenor = false;
2305  bool hasExactlyOnePlane = false;
2306  bool canEditLabelScalarValue = false;
2307  bool hasImages = false;
2308  for (int i = 0; i < selCount; ++i) {
2309  ccHObject* item = static_cast<ccHObject*>(
2310  selectedIndexes[i].internalPointer());
2311  if (!item) {
2312  assert(false);
2313  continue;
2314  }
2315  if (item->getChildrenNumber() > 1) {
2316  hasMoreThanOneChild = true;
2317  }
2318  leafObject |= item->isLeaf();
2319  if (!item->isA(CV_TYPES::HIERARCHY_OBJECT)) {
2320  toggleVisibility = true;
2321  if (item->isKindOf(CV_TYPES::POINT_CLOUD)) {
2322  toggleOtherProperties = true;
2323  } else if (item->isKindOf(CV_TYPES::MESH)) {
2324  toggleMaterials = true;
2325  toggleOtherProperties = true;
2326  }
2327  if (item->isKindOf(CV_TYPES::IMAGE)) {
2328  hasImages = true;
2329  }
2330 
2331  // check for images in children
2332  ccHObject::Container filteredChildren;
2333  if (item->filterChildren(filteredChildren, true,
2334  CV_TYPES::IMAGE) != 0) {
2335  hasImages = true;
2336  }
2337 
2338  if (selCount == 1) {
2339  if (item->isA(CV_TYPES::LABEL_2D)) {
2340  hasExactlyOnePlanarEntity =
2341  (static_cast<cc2DLabel*>(item)->size() ==
2342  3);
2343  cc2DLabel* label = ccHObjectCaster::To2DLabel(item);
2344  if (label) {
2345  canEditLabelScalarValue =
2346  (label->size() == 1 &&
2347  label->getPickedPoint(0).cloud &&
2348  label->getPickedPoint(0)
2349  .cloud->hasScalarFields() &&
2350  label->getPickedPoint(0).cloud->isA(
2352  static_cast<ccPointCloud*>(
2353  label->getPickedPoint(0).cloud)
2354  ->getCurrentDisplayedScalarField() !=
2355  0);
2356  } else {
2357  assert(false);
2358  }
2359  } else if (item->isA(CV_TYPES::PLANE) ||
2360  item->isA(CV_TYPES::FACET)) {
2361  hasExactlyOnePlanarEntity = true;
2362  hasExactlyOnePlane =
2363  item->isKindOf(CV_TYPES::PLANE);
2364  } else if (item->isA(CV_TYPES::GBL_SENSOR)) {
2365  hasExacltyOneGBLSenor = true;
2366  }
2367  }
2368  }
2369  }
2370 
2371  if (hasExactlyOnePlanarEntity) {
2372  menu.addAction(m_alignCameraWithEntity);
2373  menu.addAction(m_alignCameraWithEntityReverse);
2374  menu.addSeparator();
2375  }
2376  if (hasExactlyOnePlane) {
2377  // MainWindow::TheInstance()->addEditPlaneAction( menu );
2378  }
2379  if (hasExacltyOneGBLSenor) {
2380  menu.addAction(m_enableBubbleViewMode);
2381  }
2382 
2383  menu.addAction(m_gatherInformation);
2384  menu.addSeparator();
2385  menu.addAction(m_toggleSelectedEntities);
2386  if (toggleVisibility) {
2387  menu.addAction(m_toggleSelectedEntitiesVisibility);
2388  }
2389  if (toggleOtherProperties) {
2390  menu.addAction(m_toggleSelectedEntitiesColor);
2391  menu.addAction(m_toggleSelectedEntitiesNormals);
2392  menu.addAction(m_toggleSelectedEntitiesSF);
2393  }
2394  if (toggleMaterials) {
2395  menu.addAction(m_toggleSelectedEntitiesMat);
2396  }
2397  menu.addAction(m_toggleSelectedEntities3DName);
2398  menu.addSeparator();
2399  menu.addAction(m_deleteSelectedEntities);
2400  if (selCount == 1 && hasMoreThanOneChild) {
2401  menu.addSeparator();
2402  menu.addAction(m_sortChildrenAZ);
2403  menu.addAction(m_sortChildrenZA);
2404  menu.addAction(m_sortChildrenType);
2405  }
2406 
2407  if (selCount == 1 && !leafObject) {
2408  menu.addSeparator();
2409  menu.addAction(m_selectByTypeAndName);
2410  menu.addSeparator();
2411  menu.addAction(m_addEmptyGroup);
2412  }
2413 
2414  if (hasImages) {
2415  menu.addSeparator();
2416  menu.addAction(m_exportImages);
2417  }
2418 
2419  if (canEditLabelScalarValue) {
2420  menu.addSeparator();
2421  menu.addAction(m_editLabelScalarValue);
2422  }
2423 
2424  menu.addSeparator();
2425  }
2426 
2427  menu.addAction(m_expandBranch);
2428  menu.addAction(m_collapseBranch);
2429  } else {
2430  menu.addSeparator();
2431  menu.addAction(m_addEmptyGroup);
2432  }
2433 
2434  menu.exec(m_dbTreeWidget->mapToGlobal(menuPos));
2435 }
2436 
2437 QItemSelectionModel::SelectionFlags ccCustomQTreeView::selectionCommand(
2438  const QModelIndex& index, const QEvent* event /*=0*/) const {
2439  if (index.isValid()) {
2440  // special case: labels can only be merged with labels!
2441  QModelIndexList selectedIndexes = selectionModel()->selectedIndexes();
2442  if (!selectedIndexes.empty() && !selectionModel()->isSelected(index)) {
2443  ccHObject* selectedItem =
2444  static_cast<ccHObject*>(index.internalPointer());
2445  if (selectedItem &&
2446  selectedItem->isA(CV_TYPES::LABEL_2D) !=
2447  static_cast<ccHObject*>(
2448  selectedIndexes[0].internalPointer())
2449  ->isA(CV_TYPES::LABEL_2D))
2450  return QItemSelectionModel::ClearAndSelect;
2451  }
2452  }
2453 
2454  return QTreeView::selectionCommand(index, event);
2455 }
MouseEvent event
int64_t CV_CLASS_ENUM
Type of object type flags (64 bits)
Definition: CVTypes.h:97
std::string filename
std::shared_ptr< core::Tensor > image
int size
std::string name
int count
char type
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
const QIcon & icon(CV_CLASS_ENUM id, bool locked)
Definition: ecvDBRoot.cpp:90
static MainWindow * TheInstance()
Returns the unique instance of this object.
void refreshAll(bool only2D=false, bool forceRedraw=true) override
Redraws all GL windows that have the 'refresh' flag on.
Type u[3]
Definition: CVGeom.h:139
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
2D label (typically attached to points)
Definition: ecv2DLabel.h:22
const PickedPoint & getPickedPoint(unsigned index) const
Returns a given point.
Definition: ecv2DLabel.h:194
void updateLabel()
unsigned size() const
Returns current size.
Definition: ecv2DLabel.h:74
2D viewport label
Custom QTreeView widget (for advanced selection behavior)
Definition: ecvDBRoot.h:51
virtual QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, const QEvent *event=nullptr) const
Definition: ecvDBRoot.cpp:2437
GUI database tree root.
Definition: ecvDBRoot.h:65
QAction * m_alignCameraWithEntity
Context menu action: use 3-points labels or planes to orient camera.
Definition: ecvDBRoot.h:331
void updatePropertiesView()
Updates properties view.
Definition: ecvDBRoot.cpp:1225
QAction * m_sortChildrenType
Context menu action: sort children by type.
Definition: ecvDBRoot.h:307
void toggleSelectedEntitiesMat()
Definition: ecvDBRoot.h:239
void editLabelScalarValue()
Definition: ecvDBRoot.cpp:2106
void show()
Definition: ecvDBRoot.cpp:393
void hidePropertiesView()
Hides properties view.
Definition: ecvDBRoot.cpp:1210
void expandElement(ccHObject *object, bool state)
Expands tree at a given node.
Definition: ecvDBRoot.cpp:477
virtual QModelIndex index(int row, int column, const QModelIndex &parentIndex=QModelIndex()) const override
Definition: ecvDBRoot.cpp:921
void dbIsNotEmptyAnymore()
int countSelectedEntities(CV_CLASS_ENUM filter=CV_TYPES::OBJECT)
Definition: ecvDBRoot.cpp:1256
QAction * m_enableBubbleViewMode
Context menu action: enable bubble-view (on a sensor)
Definition: ecvDBRoot.h:335
void hide()
Definition: ecvDBRoot.cpp:388
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const override
Definition: ecvDBRoot.cpp:994
void sortChildrenZA()
Definition: ecvDBRoot.cpp:1830
QAction * m_sortChildrenZA
Context menu action: sort children in reverse alphabetical order.
Definition: ecvDBRoot.h:305
void updateCCObject(ccHObject *object)
Definition: ecvDBRoot.cpp:1237
QAction * m_gatherInformation
Context menu action: gather (recursive) information on selected entities.
Definition: ecvDBRoot.h:301
QAction * m_editLabelScalarValue
Context menu action: change current scalar value (via a 2D label)
Definition: ecvDBRoot.h:337
void gatherRecursiveInformation()
Definition: ecvDBRoot.cpp:1699
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Definition: ecvDBRoot.cpp:777
QAction * m_toggleSelectedEntities
Context menu action: enabled/disable selected entities.
Definition: ecvDBRoot.h:315
void unselectEntity(ccHObject *obj)
Unselects a given entity.
Definition: ecvDBRoot.cpp:1035
virtual ~ccDBRoot() override
Destructor.
Definition: ecvDBRoot.cpp:382
void toggleSelectedEntities()
Definition: ecvDBRoot.h:224
QTreeView * m_dbTreeWidget
Associated widget for DB tree.
Definition: ecvDBRoot.h:286
virtual QModelIndex parent(const QModelIndex &index) const override
Definition: ecvDBRoot.cpp:963
QAction * m_deleteSelectedEntities
Context menu action: delete selected entities.
Definition: ecvDBRoot.h:313
void toggleSelectedEntities3DName()
Definition: ecvDBRoot.h:242
QAction * m_toggleSelectedEntitiesNormals
Context menu action: hide/show selected entities normals.
Definition: ecvDBRoot.h:321
QPoint m_contextMenuPos
Last context menu pos.
Definition: ecvDBRoot.h:340
void toggleSelectedEntitiesVisibility()
Definition: ecvDBRoot.h:227
QAction * m_selectByTypeAndName
Context menu action: select object by type and/or by name.
Definition: ecvDBRoot.h:309
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
Definition: ecvDBRoot.cpp:1401
void removeElements(ccHObject::Container &objects)
Removes several elements at once from the DB tree.
Definition: ecvDBRoot.cpp:513
void alignCameraWithEntityIndirect()
Definition: ecvDBRoot.h:248
QAction * m_addEmptyGroup
Context menu action: add empty group.
Definition: ecvDBRoot.h:329
void sortChildrenType()
Definition: ecvDBRoot.cpp:1832
void redrawCCObjectAndChildren(ccHObject *object, bool forceRedraw=true)
Definition: ecvDBRoot.cpp:1250
void toggleSelectedEntitiesSF()
Definition: ecvDBRoot.h:236
ccHObject * m_treeRoot
Associated DB root.
Definition: ecvDBRoot.h:283
virtual QVariant data(const QModelIndex &index, int role) const override
Definition: ecvDBRoot.cpp:690
void selectEntity(ccHObject *obj, bool forceAdditiveSelection=false)
Selects a given entity.
Definition: ecvDBRoot.cpp:1054
virtual Qt::DropActions supportedDropActions() const override
Definition: ecvDBRoot.cpp:1338
void selectEntities(std::unordered_set< int > entIDs)
Selects multiple entities at once (shortcut to the other version)
Definition: ecvDBRoot.cpp:1109
QAction * m_toggleSelectedEntitiesMat
Context menu action: hide/show selected entities materials/textures.
Definition: ecvDBRoot.h:323
ccHObject * getRootEntity()
Returns associated root object.
Definition: ecvDBRoot.cpp:421
void redrawCCObject(ccHObject *object, bool forceRedraw=true)
Definition: ecvDBRoot.cpp:1245
void selectChildrenByTypeAndName(CV_CLASS_ENUM type, bool typeIsExclusive=true, QString name=QString(), bool nameIsRegex=false)
Selects objects by type and/or name.
Definition: ecvDBRoot.cpp:1971
QAction * m_alignCameraWithEntityReverse
Context menu action: reverse of m_alignCameraWithEntity.
Definition: ecvDBRoot.h:333
size_t getSelectedEntities(ccHObject::Container &selectedEntities, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, dbTreeSelectionInfo *info=nullptr)
Definition: ecvDBRoot.cpp:1273
void selectByTypeAndName()
Definition: ecvDBRoot.cpp:1900
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const override
Definition: ecvDBRoot.cpp:983
QAction * m_toggleSelectedEntitiesColor
Context menu action: hide/show selected entities color.
Definition: ecvDBRoot.h:319
SortRules
Entities sorting schemes.
Definition: ecvDBRoot.h:268
@ SORT_BY_TYPE
Definition: ecvDBRoot.h:268
@ SORT_Z2A
Definition: ecvDBRoot.h:268
@ SORT_A2Z
Definition: ecvDBRoot.h:268
ccHObject * find(int uniqueID) const
Finds an element in DB.
Definition: ecvDBRoot.cpp:1197
void alignCameraWithEntity(bool reverse)
Aligns the camera with the currently selected entity.
Definition: ecvDBRoot.cpp:1628
void showPropertiesView(ccHObject *obj)
Shows properties view for a given element.
Definition: ecvDBRoot.cpp:1201
virtual QMap< int, QVariant > itemData(const QModelIndex &index) const override
Definition: ecvDBRoot.cpp:1389
void reflectObjectPropChange(ccHObject *obj)
Definition: ecvDBRoot.cpp:1216
void expandOrCollapseHoveredBranch(bool expand)
Expands or collapses hovered item.
Definition: ecvDBRoot.cpp:1590
QAction * m_sortChildrenAZ
Context menu action: sort children in alphabetical order.
Definition: ecvDBRoot.h:303
void unselectAllEntities()
Unselects all entities.
Definition: ecvDBRoot.cpp:1047
void toggleSelectedEntitiesColor()
Definition: ecvDBRoot.h:230
QAction * m_toggleSelectedEntities3DName
Context menu action: hide/show selected entities 3D name.
Definition: ecvDBRoot.h:327
void sortChildrenAZ()
Definition: ecvDBRoot.cpp:1828
void unloadAll()
Unloads all entities.
Definition: ecvDBRoot.cpp:398
TOGGLE_PROPERTY
Entity property that can be toggled.
Definition: ecvDBRoot.h:198
@ TG_ENABLE
Definition: ecvDBRoot.h:199
@ TG_3D_NAME
Definition: ecvDBRoot.h:205
@ TG_COLOR
Definition: ecvDBRoot.h:201
@ TG_NORMAL
Definition: ecvDBRoot.h:203
@ TG_VISIBLE
Definition: ecvDBRoot.h:200
void toggleSelectedEntitiesProperty(TOGGLE_PROPERTY prop)
Definition: ecvDBRoot.cpp:2023
void enableBubbleViewMode()
Definition: ecvDBRoot.cpp:2088
QStandardItemModel * m_propertiesModel
Selected entity's properties data model.
Definition: ecvDBRoot.h:292
void changeSelection(const QItemSelection &selected, const QItemSelection &deselected)
Definition: ecvDBRoot.cpp:999
void removeElement(ccHObject *object)
Removes an element from the DB tree.
Definition: ecvDBRoot.cpp:554
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: ecvDBRoot.cpp:1342
void sortSelectedEntitiesChildren(SortRules rule)
Sorts selected entities children.
Definition: ecvDBRoot.cpp:1836
QAction * m_expandBranch
Context menu action: expand tree branch.
Definition: ecvDBRoot.h:297
QTreeView * m_propertiesTreeWidget
Associated widget for selected entity's properties tree.
Definition: ecvDBRoot.h:289
void showContextMenu(const QPoint &)
Definition: ecvDBRoot.cpp:2284
void toggleSelectedEntitiesNormals()
Definition: ecvDBRoot.h:233
void expandBranch()
Definition: ecvDBRoot.cpp:1586
QAction * m_collapseBranch
Context menu action: collapse tree branch.
Definition: ecvDBRoot.h:299
void collapseBranch()
Definition: ecvDBRoot.cpp:1588
QAction * m_exportImages
Context menu action: export images.
Definition: ecvDBRoot.h:311
void exportImages()
Definition: ecvDBRoot.cpp:2159
void dbIsEmpty()
ccPropertiesTreeDelegate * m_ccPropDelegate
Selected entity's properties delegate.
Definition: ecvDBRoot.h:294
void alignCameraWithEntityDirect()
Definition: ecvDBRoot.h:247
QAction * m_toggleSelectedEntitiesSF
Context menu action: hide/show selected entities SF.
Definition: ecvDBRoot.h:325
void addEmptyGroup()
Definition: ecvDBRoot.cpp:2074
QAction * m_toggleSelectedEntitiesVisibility
Context menu action: hide/show selected entities.
Definition: ecvDBRoot.h:317
void deleteSelectedEntities()
Definition: ecvDBRoot.cpp:593
void addElement(ccHObject *object, bool autoExpand=true)
Adds an element to the DB tree.
Definition: ecvDBRoot.cpp:423
void selectionChanged()
virtual void toggleVisibility()
Toggles visibility.
virtual bool isVisible() const
Returns whether entity is visible or not.
virtual void toggleNormals()
Toggles normals display state.
virtual bool hasColors() const
Returns whether colors are enabled or not.
virtual bool hasNormals() const
Returns whether normals are enabled or not.
virtual void toggleSF()
Toggles SF display state.
virtual void toggleColors()
Toggles colors display state.
virtual bool isSelected() const
Returns whether entity is selected or not.
virtual void toggleShowName()
Toggles name in 3D display state.
virtual bool nameShownIn3D() const
Returns whether name is displayed in 3D or not.
virtual bool hasScalarFields() const
Returns whether one or more scalar fields are instantiated.
virtual void setSelected(bool state)
Selects/Unselects entity.
Facet.
Definition: ecvFacet.h:25
CCVector3 getNormal() const override
Returns the entity normal.
Definition: ecvFacet.h:63
Ground-based Laser sensor.
Definition: ecvGBLSensor.h:26
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
Vector3Tpl< T > getColumnAsVec3D(unsigned index) const
Returns a copy of a given column as a CCVector3.
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
virtual ccGenericPointCloud * getAssociatedCloud() const =0
Returns the vertices cloud.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
Generic primitive interface.
virtual void hideShowDrawings(CC_DRAW_CONTEXT &context)
virtual ccGLMatrix & getTransformation()
Returns the transformation that is currently applied to the vertices.
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
static cc2DViewportLabel * To2DViewportLabel(ccHObject *obj)
Converts current object to cc2DViewportLabel (if possible)
static ccGenericPrimitive * ToPrimitive(ccHObject *obj)
Converts current object to ccGenericPrimitive (if possible)
static ccPolyline * ToPolyline(ccHObject *obj)
Converts current object to ccPolyline (if possible)
static cc2DLabel * To2DLabel(ccHObject *obj)
Converts current object to cc2DLabel (if possible)
static ccGenericPointCloud * ToGenericPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccGenericPointCloud.
static ccSensor * ToSensor(ccHObject *obj)
Converts current object to ccSensor (if possible)
static ccImage * ToImage(ccHObject *obj)
static ccGenericMesh * ToGenericMesh(ccHObject *obj)
Converts current object to ccGenericMesh (if possible)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void hideBB(CC_DRAW_CONTEXT context)
ccHObject * find(unsigned uniqueID)
Finds an entity in this object hierarchy.
int getChildIndex(const ccHObject *aChild) const
Returns child index.
int getIndex() const
Returns index relatively to its parent or -1 if no parent.
int getDependencyFlagsWith(const ccHObject *otherObject)
Returns the dependency flags with a given object.
void addDependency(ccHObject *otherObject, int flags, bool additive=true)
Adds a new dependence (additive or not)
bool isAncestorOf(const ccHObject *anObject) const
Returns true if the current object is an ancestor of the specified one.
QString getViewId() const
Definition: ecvHObject.h:225
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
unsigned int getChildCountRecursive() const
Returns the total number of children under this object recursively.
void setRedrawFlagRecursive(bool redraw=false)
@ DP_PARENT_OF_OTHER
Definition: ecvHObject.h:266
virtual ccBBox getBB_recursive(bool withGLFeatures=false, bool onlyEnabledChildren=true)
Returns the bounding-box of this entity and it's children.
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
virtual void redrawDisplay(bool forceRedraw=true, bool only2D=false)
Redraws associated display.
void removeDependencyFlag(ccHObject *otherObject, DEPENDENCY_FLAGS flag)
Removes a given dependency flag.
void removeChild(ccHObject *child)
virtual QIcon getIcon() const
Returns the icon associated to this entity.
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
void swapChildren(unsigned firstChildIndex, unsigned secondChildIndex)
Swaps two children.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
void setForceRedrawRecursive(bool redraw=false)
void showBB(CC_DRAW_CONTEXT context)
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
CV_CLASS_ENUM getClassID() const override
Returns class ID.
Definition: ecvHObject.h:232
Generic image.
Definition: ecvImage.h:19
Triangular mesh.
Definition: ecvMesh.h:35
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
virtual unsigned size() const override
Returns the number of triangles.
bool hasTriNormals() const override
Returns whether the mesh has per-triangle normals.
virtual bool isLocked() const
Returns whether the object is locked or not.
Definition: ecvObject.h:112
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual unsigned getUniqueID() const
Returns object unique ID.
Definition: ecvObject.h:86
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
bool isLeaf() const
Definition: ecvObject.h:122
virtual bool isEnabled() const
Returns whether the object is enabled or not.
Definition: ecvObject.h:97
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
Plane (primitive)
Definition: ecvPlane.h:18
CCVector3 getNormal() const override
Returns the entity normal.
Definition: ecvPlane.h:73
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool hasColors() const override
Returns whether colors are enabled or not.
ccScalarField * getCurrentDisplayedScalarField() const
Returns the currently displayed scalar (or 0 if none)
size_t gridCount() const
Returns the number of associated grids.
Colored polyline.
Definition: ecvPolyline.h:24
GUI properties list dialog element.
void ccObjectAppearanceChanged(ccHObject *hObject, bool forceRedraw=true) const
void fillModel(ccHObject *hObject)
Fill property view with QItems corresponding to object's type.
ccHObject * getCurrentObject()
Returns currently bound object.
void ccObjectPropertiesChanged(ccHObject *hObject) const
void ccObjectAndChildrenAppearanceChanged(ccHObject *hObject, bool forceRedraw=true) const
A scalar field associated to display-related parameters.
void computeMinAndMax() override
Determines the min and max values.
Minimal dialog to pick one element in a list (combo box)
bool getNameMatchIsUsed() const
if performing name-match (regex or not)
bool getNameIsRegex() const
if the name must be considered as regex
bool getStrictMatchState() const
Returns the state of the strict type checkbox.
CV_CLASS_ENUM getSelectedType()
Returns the selected type.
void addType(QString typeName, CV_CLASS_ENUM type)
Add an element to the 'type' combo box.
QString getSelectedName()
Returns the selected name (if any)
Generic sensor interface.
Definition: ecvSensor.h:27
virtual void hideShowDrawings(CC_DRAW_CONTEXT &context)
Vector3Tpl< T > getCenter() const
Returns center.
Definition: BoundingBox.h:164
static void ComputeBaseVectors(const CCVector3 &N, CCVector3 &X, CCVector3 &Y)
Computes base vectors for a given 3D plane.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
unsigned size() const override
Definition: PointCloudTpl.h:38
virtual GenericIndexedCloudPersist * getAssociatedCloud()
Returns the associated (source) cloud.
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
void setValue(std::size_t index, ScalarType value)
Definition: ScalarField.h:96
const char * getName() const
Returns scalar field name.
Definition: ScalarField.h:43
static void SetRemoveViewIDs(std::vector< removeInfo > &removeinfos)
static void SetRemoveAllFlag(bool state)
virtual void getDataAxesGridProperties(const QString &viewID, AxesGridProperties &props, int viewport=0) const
Get Data Axes Grid properties (Virtual interface for derived classes)
static ecvDisplayTools * TheInstance()
static void SetRedrawRecursive(bool redraw=false)
static void UpdateScreen()
static void RemoveWidgets(const WIDGETS_PARAMETER &param, bool update=false)
Graphical progress indicator (thread-safe)
virtual void stop() override
Notifies the fact that the process has ended.
virtual void start() override
virtual bool isCancelRequested() override
Checks if the process should be canceled.
__host__ __device__ float3 cross(float3 a, float3 b)
Definition: cutil_math.h:1295
static bool CanDetachCloud(const ccHObject *obj)
Definition: ecvDBRoot.cpp:63
static const int c_propViewLeftColumnWidth
Definition: ecvDBRoot.cpp:60
@ WIDGET_RECTANGLE_2D
@ WIDGET_T2D
ImGuiContext * context
Definition: Window.cpp:76
@ SENSOR
Definition: CVTypes.h:116
@ HIERARCHY_OBJECT
Definition: CVTypes.h:103
@ CUSTOM_H_OBJECT
Definition: CVTypes.h:179
@ VIEWPORT_2D_OBJECT
Definition: CVTypes.h:141
@ PRIMITIVE
Definition: CVTypes.h:119
@ MESH
Definition: CVTypes.h:105
@ GBL_SENSOR
Definition: CVTypes.h:117
@ NORMAL_INDEXES_ARRAY
Definition: CVTypes.h:135
@ CALIBRATED_IMAGE
Definition: CVTypes.h:115
@ RGB_COLOR_ARRAY
Definition: CVTypes.h:137
@ IMAGE
Definition: CVTypes.h:114
@ COORDINATESYSTEM
Definition: CVTypes.h:145
@ TRANS_BUFFER
Definition: CVTypes.h:144
@ CONE
Definition: CVTypes.h:123
@ MESH_GROUP
Definition: CVTypes.h:107
@ POINT_CLOUD
Definition: CVTypes.h:104
@ TEX_COORDS_ARRAY
Definition: CVTypes.h:139
@ DISH
Definition: CVTypes.h:129
@ NORMALS_ARRAY
Definition: CVTypes.h:134
@ CIRCLE
Definition: CVTypes.h:113
@ LABEL_2D
Definition: CVTypes.h:140
@ POLY_LINE
Definition: CVTypes.h:112
@ POINT_KDTREE
Definition: CVTypes.h:111
@ FACET
Definition: CVTypes.h:109
@ SUB_MESH
Definition: CVTypes.h:106
@ QUADRIC
Definition: CVTypes.h:131
@ POINT_OCTREE
Definition: CVTypes.h:110
@ TORUS
Definition: CVTypes.h:122
@ EXTRU
Definition: CVTypes.h:130
@ SPHERE
Definition: CVTypes.h:121
@ MATERIAL_SET
Definition: CVTypes.h:132
@ CAMERA_SENSOR
Definition: CVTypes.h:118
@ PLANE
Definition: CVTypes.h:120
@ DISC
Definition: CVTypes.h:146
@ VIEWPORT_2D_LABEL
Definition: CVTypes.h:142
@ CYLINDER
Definition: CVTypes.h:126
@ OBJECT
Definition: CVTypes.h:102
void swap(optional< T > &x, optional< T > &y) noexcept(noexcept(x.swap(y)))
Definition: Optional.h:890
struct Select Select
Definition: sqlite3.c:14660
Data Axes Grid properties structure Encapsulates all properties for vtkCubeAxesActor configuration.
Picked point descriptor.
Definition: ecv2DLabel.h:122
unsigned index
Point/triangle index.
Definition: ecv2DLabel.h:128
ccGenericPointCloud * cloud
Cloud.
Definition: ecv2DLabel.h:124
Display context.
Precise statistics about current selection.
Definition: ecvDBRoot.h:28
size_t gblSensorCount
Definition: ecvDBRoot.h:43
size_t polylineCount
Definition: ecvDBRoot.h:37
size_t cameraSensorCount
Definition: ecvDBRoot.h:44