ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvGraphicalSegmentationTool.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 
9 
11 
12 // LOCAL
13 #include "MainWindow.h"
14 #include "ecvItemSelectionDlg.h"
15 
16 // CV_CORE_LIB
18 #include <SquareMatrix.h>
19 
20 // CV_DB_LIB
21 #include <CVLog.h>
22 #include <ecv2DViewportObject.h>
23 #include <ecvDisplayTools.h>
24 #include <ecvGenericPointCloud.h>
25 #include <ecvHObjectCaster.h>
26 #include <ecvMesh.h>
27 #include <ecvPointCloud.h>
28 #include <ecvPolyline.h>
29 
30 // for the helper (apply)
31 #include <ecv2DLabel.h>
32 #include <ecvCameraSensor.h>
33 #include <ecvGBLSensor.h>
34 #include <ecvSubMesh.h>
35 
36 // CVPluginAPI
37 #include <ecvMainAppInterface.h>
38 
39 // Qt
40 #include <QInputDialog>
41 #include <QMenu>
42 #include <QMessageBox>
43 #include <QPushButton>
44 #include <QSettings>
45 
46 // System
47 #include <assert.h>
48 
49 #if defined(_OPENMP)
50 // OpenMP
51 #include <omp.h>
52 #endif
53 
55  : ccOverlayDialog(parent),
56  Ui::GraphicalSegmentationDlg(),
57  m_somethingHasChanged(false),
58  m_state(0),
59  m_segmentationPoly(nullptr),
60  m_polyVertices(nullptr),
61  m_rectangularSelection(false),
62  m_deleteHiddenParts(false) {
63  // Set QDialog background as transparent (DGM: doesn't work over an OpenGL
64  // context)
65  // setAttribute(Qt::WA_NoSystemBackground);
66 
67  setupUi(this);
68 
69  connect(inButton, &QToolButton::clicked, this,
71  connect(outButton, &QToolButton::clicked, this,
73  connect(razButton, &QToolButton::clicked, this,
75  connect(validButton, &QToolButton::clicked, this,
77  connect(validAndDeleteButton, &QToolButton::clicked, this,
79  connect(cancelButton, &QToolButton::clicked, this,
81  connect(pauseButton, &QToolButton::toggled, this,
83 
84  // selection modes
85  connect(actionSetPolylineSelection, &QAction::triggered, this,
87  connect(actionSetRectangularSelection, &QAction::triggered, this,
89  // import/export options
90  connect(actionUseExistingPolyline, &QAction::triggered, this,
92  connect(actionExportSegmentationPolyline, &QAction::triggered, this,
94 
95  // add shortcuts
96  addOverridenShortcut(Qt::Key_Space); // space bar for the "pause" button
97  addOverridenShortcut(Qt::Key_Escape); // escape key for the "cancel" button
98  addOverridenShortcut(Qt::Key_Return); // return key for the "apply" button
100  Qt::Key_Delete); // delete key for the "apply and delete" button
101  addOverridenShortcut(Qt::Key_Tab); // tab key to switch between rectangular
102  // and polygonal selection modes
103  addOverridenShortcut(Qt::Key_I); //'I' key for the "segment in" button
104  addOverridenShortcut(Qt::Key_O); //'O' key for the "segment out" button
105  connect(this, &ccOverlayDialog::shortcutTriggered, this,
107 
108  QMenu* selectionModeMenu = new QMenu(this);
109  selectionModeMenu->addAction(actionSetPolylineSelection);
110  selectionModeMenu->addAction(actionSetRectangularSelection);
111  selectionModelButton->setDefaultAction(actionSetPolylineSelection);
112  selectionModelButton->setMenu(selectionModeMenu);
113 
114  QMenu* importExportMenu = new QMenu(this);
115  importExportMenu->addAction(actionUseExistingPolyline);
116  importExportMenu->addAction(actionExportSegmentationPolyline);
117  loadSaveToolButton->setMenu(importExportMenu);
118 
119  m_polyVertices = new ccPointCloud("vertices");
126  allowPolylineExport(false);
127 }
128 
130  if (state) {
131  actionExportSegmentationPolyline->setEnabled(true);
132  } else {
133  loadSaveToolButton->setDefaultAction(actionUseExistingPolyline);
134  actionExportSegmentationPolyline->setEnabled(false);
135  }
136 }
137 
140  m_segmentationPoly = nullptr;
141 
142  if (m_polyVertices) delete m_polyVertices;
143  m_polyVertices = nullptr;
144 }
145 
147  switch (key) {
148  case Qt::Key_Space:
149  pauseButton->toggle();
150  return;
151 
152  case Qt::Key_I:
153  inButton->click();
154  return;
155 
156  case Qt::Key_O:
157  outButton->click();
158  return;
159 
160  case Qt::Key_Return:
161  validButton->click();
162  return;
163  case Qt::Key_Delete:
164  validAndDeleteButton->click();
165  return;
166  case Qt::Key_Escape:
167  cancelButton->click();
168  return;
169 
170  case Qt::Key_Tab:
173  else
175  return;
176 
177  default:
178  // nothing to do
179  break;
180  }
181 }
182 
184  assert(m_segmentationPoly);
185 
186  if (!ccOverlayDialog::linkWith(win)) {
187  return false;
188  }
189 
192  SIGNAL(leftButtonClicked(int, int)), this,
193  SLOT(addPointToPolyline(int, int)));
195  SIGNAL(rightButtonClicked(int, int)), this,
196  SLOT(closePolyLine(int, int)));
198  SIGNAL(mouseMoved(int, int, Qt::MouseButtons)), this,
199  SLOT(updatePolyLine(int, int, Qt::MouseButtons)));
200  connect(ecvDisplayTools::TheInstance(), SIGNAL(buttonReleased()), this,
201  SLOT(closeRectangle()));
202  }
203 
204  return true;
205 }
206 
209 
212  allowPolylineExport(false);
213 
214  // ecvDisplayTools::AddToOwnDB(m_segmentationPoly);
216  pauseSegmentationMode(false);
217 
218  m_somethingHasChanged = false;
219 
220  reset();
221 
222  return ccOverlayDialog::start();
223 }
224 
226  ccHObject* entity, bool unallocateVisibilityArrays) {
227  if (!entity) {
228  assert(false);
229  return;
230  }
231 
232  // restore the display state of the entity
233  entity->popDisplayState();
234 
235  if (unallocateVisibilityArrays) {
236  ccGenericPointCloud* asCloud =
238  if (asCloud) {
239  asCloud->unallocateVisibilityArray();
240  }
241  }
242 
243  // specific case: we may have automatically hidden the mesh or the polyline
244  // associated to a cloud
245  if (entity->isKindOf(CV_TYPES::POINT_CLOUD)) {
246  ccGenericPointCloud* cloud = static_cast<ccGenericPointCloud*>(entity);
247 
248  ccGenericMesh* associatedMesh = nullptr;
249  if (ccGenericMesh::IsCloudVerticesOfMesh(cloud, &associatedMesh) &&
250  associatedMesh) {
251  associatedMesh->popDisplayState();
252  return;
253  }
254 
255  ccPolyline* associatedPolyline = nullptr;
256  if (ccPolyline::IsCloudVerticesOfPolyline(cloud, &associatedPolyline) &&
257  associatedPolyline) {
258  associatedPolyline->popDisplayState();
259  return;
260  }
261  }
262 }
263 
265  for (QSet<ccHObject*>::const_iterator p = m_toSegment.constBegin();
266  p != m_toSegment.constEnd(); ++p) {
267  ccHObject* entity = *p;
268 
269  prepareEntityForRemoval(entity, true);
270  }
271 
272  setDrawFlag(true); // for update afterforwards
273  m_toSegment.clear();
274 }
275 
277  assert(m_segmentationPoly);
278 
281  "Segmentation [OFF]", ecvDisplayTools::UPPER_CENTER_MESSAGE,
283 
286  ecvDisplayTools::GetCurrentScreen()->setMouseTracking(false);
290  ecvDisplayTools::RedrawDisplay(true, false);
291  }
292  ccOverlayDialog::stop(accepted);
293 }
294 
295 void ccGraphicalSegmentationTool::setDrawFlag(bool state /* = true*/) {
296  for (QSet<ccHObject*>::iterator p = m_toSegment.begin();
297  p != m_toSegment.end(); ++p) {
298  (*p)->setRedrawFlagRecursive(state);
299  }
300 }
301 
303  if (m_somethingHasChanged) {
304  for (QSet<ccHObject*>::const_iterator p = m_toSegment.constBegin();
305  p != m_toSegment.constEnd(); ++p) {
307  }
308 
312  setDrawFlag(true);
314  }
315 
316  m_somethingHasChanged = false;
317  }
318 
319  razButton->setEnabled(false);
320  validButton->setEnabled(false);
321  validAndDeleteButton->setEnabled(false);
322  loadSaveToolButton->setDefaultAction(actionUseExistingPolyline);
323 }
324 
326  bool silent /*=false*/) {
327  bool result = false;
328  if (entity->isKindOf(CV_TYPES::POINT_CLOUD)) {
329  ccGenericPointCloud* cloud =
331 
332  ccGenericMesh* associatedMesh = nullptr;
333  if (ccGenericMesh::IsCloudVerticesOfMesh(cloud, &associatedMesh)) {
334  assert(nullptr != associatedMesh);
335  if (m_toSegment.contains(associatedMesh)) {
336  if (!silent) {
337  CVLog::Warning(QString("[Graphical Segmentation Tool] The "
338  "mesh associated to cloud %1 is "
339  "already selected")
340  .arg(cloud->getName()));
341  }
342  return false;
343  }
344 
345  // hide the associated mesh, as it will also be (graphically)
346  // segmented
347  associatedMesh->pushDisplayState();
348  associatedMesh->setVisible(false);
349  }
350 
351  ccPolyline* associatedPolyline = nullptr;
352  if (ccPolyline::IsCloudVerticesOfPolyline(cloud, &associatedPolyline)) {
353  assert(nullptr != associatedPolyline);
354  if (m_toSegment.contains(associatedPolyline)) {
355  if (!silent) {
356  CVLog::Warning(QString("[Graphical Segmentation Tool] The "
357  "polyline associated to cloud %1 is "
358  "already selected")
359  .arg(cloud->getName()));
360  }
361  return false;
362  }
363 
364  // hide the associated polyline, as it will also be (graphically)
365  // segmented
366  associatedPolyline->pushDisplayState();
367  associatedPolyline->setVisible(false);
368  }
369 
370  m_toSegment.insert(cloud);
371  cloud->pushDisplayState();
372  cloud->setVisible(true);
373  cloud->setEnabled(true);
374  } else if (entity->isKindOf(CV_TYPES::MESH)) {
375  if (entity->isKindOf(CV_TYPES::PRIMITIVE)) {
376  if (!silent) {
378  "[ccGraphicalSegmentationTool] Can't segment "
379  "primitives "
380  "yet! Sorry...");
381  }
382  return false;
383  }
384  if (entity->isKindOf(CV_TYPES::SUB_MESH)) {
385  if (!silent) {
387  "[ccGraphicalSegmentationTool] Can't segment "
388  "sub-meshes! "
389  "Select the parent mesh...");
390  }
391  return false;
392  } else {
394  assert(mesh);
395 
396  ccGenericPointCloud* vertices = mesh->getAssociatedCloud();
397  if (!vertices) {
398  assert(false);
399  return false;
400  }
401 
402  // Make sure the vertices of this mesh are not already in the 'to
403  // segment' list
404  if (m_toSegment.contains(vertices)) {
405  // let's remove the vertices
406  mesh->pushDisplayState(); // just in case the vertices were
407  // inserted before the mesh)
408  vertices->popDisplayState();
409  m_toSegment.remove(vertices);
410  }
411 
412  m_toSegment.insert(mesh);
413  mesh->pushDisplayState();
414  mesh->setVisible(true);
415  mesh->setEnabled(true);
416  result = true;
417  }
418  } else if (entity->isKindOf(CV_TYPES::POLY_LINE)) {
419  ccPolyline* poly = ccHObjectCaster::ToPolyline(entity);
420  assert(poly);
421 
422  ccGenericPointCloud* verticesCloud =
423  dynamic_cast<ccGenericPointCloud*>(poly->getAssociatedCloud());
424  if (!verticesCloud) {
425  assert(false);
426  return false;
427  }
428 
429  // Make sure the vertices of this polyline are not already in the 'to
430  // segment' list
431  if (verticesCloud && m_toSegment.contains(verticesCloud)) {
432  // let's remove the vertices
433  poly->pushDisplayState(); // just in case the vertices were
434  // inserted before the polyline)
435  verticesCloud->popDisplayState();
436  m_toSegment.remove(verticesCloud);
437  }
438 
439  m_toSegment.insert(poly);
440  poly->pushDisplayState();
441  poly->setVisible(true);
442  poly->setEnabled(true);
443 
444  result = true;
445  } else if (entity->isA(CV_TYPES::HIERARCHY_OBJECT)) {
446  // automatically add entity's children
447  for (unsigned i = 0; i < entity->getChildrenNumber(); ++i)
448  result |= addEntity(entity->getChild(i));
449  }
450 
451  return result;
452 }
453 
455  return static_cast<unsigned>(m_toSegment.size());
456 }
457 
459  int y,
460  Qt::MouseButtons buttons) {
461  // process not started yet?
462  if ((m_state & RUNNING) == 0) {
463  return;
464  }
465 
466  assert(m_polyVertices);
467  assert(m_segmentationPoly);
468 
469  unsigned vertCount = m_polyVertices->size();
470 
471  // new point (expressed relatively to the screen center)
473  CCVector3 P(static_cast<PointCoordinateType>(pos2D.x),
474  static_cast<PointCoordinateType>(pos2D.y), 0);
475 
476  if (m_state & RECTANGLE) {
477  // we need 4 points for the rectangle!
478  if (vertCount != 4) m_polyVertices->resize(4);
479 
481  CCVector3* B = const_cast<CCVector3*>(
483  CCVector3* C = const_cast<CCVector3*>(
485  CCVector3* D = const_cast<CCVector3*>(
487  *B = CCVector3(A->x, P.y, 0);
488  *C = P;
489  *D = CCVector3(P.x, A->y, 0);
490 
491  if (vertCount != 4) {
493  if (!m_segmentationPoly->addPointIndex(0, 4)) {
494  CVLog::Error("Out of memory!");
495  allowPolylineExport(false);
496  return;
497  }
499  }
500  } else if (m_state & POLYLINE) {
501  if (vertCount < 2) return;
502  // we replace last point by the current one
503  CCVector3* lastP = const_cast<CCVector3*>(
504  m_polyVertices->getPointPersistentPtr(vertCount - 1));
505  *lastP = P;
506  }
507 
509 }
510 
512  if ((m_state & STARTED) == 0) {
513  return;
514  }
515 
516  assert(m_polyVertices);
517  assert(m_segmentationPoly);
518  unsigned vertCount = m_polyVertices->size();
519 
520  // particular case: we close the rectangular selection by a 2nd click
521  if (m_rectangularSelection && vertCount == 4 && (m_state & RUNNING)) return;
522 
523  // new point
524  // QPointF pos2D = ecvDisplayTools::ToCenteredGLCoordinates(x, y);
526  CCVector3 P(static_cast<PointCoordinateType>(pos2D.x),
527  static_cast<PointCoordinateType>(pos2D.y), 0);
528 
529  // CTRL key pressed at the same time?
530  bool ctrlKeyPressed = m_rectangularSelection ||
531  ((QApplication::keyboardModifiers() &
532  Qt::ControlModifier) == Qt::ControlModifier);
533 
534  // start new polyline?
535  if (((m_state & RUNNING) == 0) || vertCount == 0 || ctrlKeyPressed) {
536  // reset state
537  m_state = (ctrlKeyPressed ? RECTANGLE : POLYLINE);
538  m_state |= (STARTED | RUNNING);
539  // reset polyline
541  if (!m_polyVertices->reserve(2)) {
542  CVLog::Error("Out of memory!");
543  allowPolylineExport(false);
544  return;
545  }
546  // we add the same point twice (the last point will be used for display
547  // only)
552  if (!m_segmentationPoly->addPointIndex(0, 2)) {
553  CVLog::Error("Out of memory!");
554  allowPolylineExport(false);
555  return;
556  }
557  } else // next points in "polyline mode" only
558  {
559  // we were already in 'polyline' mode?
560  if (m_state & POLYLINE) {
561  if (!m_polyVertices->reserve(vertCount + 1)) {
562  CVLog::Error("Out of memory!");
563  allowPolylineExport(false);
564  return;
565  }
566 
567  // we replace last point by the current one
568  CCVector3* lastP = const_cast<CCVector3*>(
569  m_polyVertices->getPointPersistentPtr(vertCount - 1));
570  *lastP = P;
571 
572  // and add a new (equivalent) one
574  if (!m_segmentationPoly->addPointIndex(vertCount)) {
575  CVLog::Error("Out of memory!");
576  return;
577  }
579  } else // we must change mode
580  {
581  assert(false); // we shouldn't fall here?!
582  m_state &= (~RUNNING);
583  addPointToPolyline(x, y);
584  return;
585  }
586  }
587 
589 }
590 
592  // only for rectangle selection in RUNNING mode
593  if ((m_state & RECTANGLE) == 0 || (m_state & RUNNING) == 0) return;
594 
595  assert(m_segmentationPoly);
596  unsigned vertCount = m_segmentationPoly->size();
597  if (vertCount < 4) {
598  // first point only? we keep the real time update mechanism
599  if (m_rectangularSelection) return;
602  allowPolylineExport(false);
603  } else {
604  allowPolylineExport(true);
605  }
606 
607  // stop
608  m_state &= (~RUNNING);
609 
611 }
612 
614  // only for polyline in RUNNING mode
615  if ((m_state & POLYLINE) == 0 || (m_state & RUNNING) == 0) return;
616 
617  assert(m_segmentationPoly);
618  unsigned vertCount = m_segmentationPoly->size();
619  if (vertCount < 4) {
622  } else {
623  // remove last point!
624  m_segmentationPoly->resize(vertCount - 1); // can't fail --> smaller
626  }
627 
628  // stop
629  m_state &= (~RUNNING);
630 
631  // set the default import/export icon to 'export' mode
632  loadSaveToolButton->setDefaultAction(actionExportSegmentationPolyline);
634 
636 }
637 
643  param.opacity = 1.0;
644  ecvDisplayTools::DrawWidgets(param, true);
645  }
646 }
647 
653  }
654 }
655 
657 
659 
660 void ccGraphicalSegmentationTool::segment(bool keepPointsInside) {
661  if (!ecvDisplayTools::GetCurrentScreen()) return;
662 
663  if (!m_segmentationPoly) {
664  CVLog::Error("No polyline defined!");
665  return;
666  }
667 
668  if (!m_segmentationPoly->isClosed()) {
669  CVLog::Error(
670  "Define and/or close the segmentation polygon first! (right "
671  "click to close)");
672  return;
673  }
674 
675  // viewing parameters
676  ccGLCameraParameters camera;
678 
679  // for each selected entity
680  for (QSet<ccHObject*>::const_iterator p = m_toSegment.constBegin();
681  p != m_toSegment.constEnd(); ++p) {
683  assert(cloud);
684 
686  cloud->getTheVisibilityArray();
687  assert(!visibilityArray.empty());
688 
689  unsigned cloudSize = cloud->size();
690 
691  // we project each point and we check if it falls inside the
692  // segmentation polyline
693 #if defined(_OPENMP)
694 #pragma omp parallel for
695 #endif
696  for (int i = 0; i < static_cast<int>(cloudSize); ++i) {
697  if (visibilityArray[i] == POINT_VISIBLE) {
698  const CCVector3* P3D = cloud->getPoint(i);
699  CCVector3d Q2D;
700  camera.project(*P3D, Q2D);
701 
702  CCVector2 P2D(static_cast<PointCoordinateType>(Q2D.x),
703  static_cast<PointCoordinateType>(Q2D.y));
704 
705  bool pointInside =
707  P2D, m_segmentationPoly);
708 
709  visibilityArray[i] =
710  (keepPointsInside != pointInside ? POINT_HIDDEN
711  : POINT_VISIBLE);
712  }
713  }
714  }
715 
716  m_somethingHasChanged = true;
717  validButton->setEnabled(true);
718  validAndDeleteButton->setEnabled(true);
719  razButton->setEnabled(true);
721  setDrawFlag(true);
723  pauseSegmentationMode(true, false);
724 }
725 
727  bool state, bool only2D /* = true*/) {
729 
730  if (!ecvDisplayTools::GetMainWindow()) return;
731 
732  if (state /*=activate pause mode*/) {
733  m_state = PAUSED;
734  if (m_polyVertices->size() != 0) {
737  allowPolylineExport(false);
738  }
741  ecvDisplayTools::GetCurrentScreen()->setMouseTracking(false);
743  "Segmentation [PAUSED]", ecvDisplayTools::UPPER_CENTER_MESSAGE,
746  "Unpause to segment again",
749  } else {
750  m_state = STARTED;
755  "Segmentation [ON] (rectangular selection)",
759  "Left click: set opposite corners",
762  } else {
764  "Segmentation [ON] (polygonal selection)",
768  "Left click: add contour points / Right click: close",
771  }
772  }
773 
774  // update mini-GUI
775  pauseButton->blockSignals(true);
776  pauseButton->setChecked(state);
777  pauseButton->blockSignals(false);
778 
781  ecvDisplayTools::RedrawDisplay(only2D, state);
782 }
783 
785  if (!m_rectangularSelection) return;
786 
787  selectionModelButton->setDefaultAction(actionSetPolylineSelection);
788 
789  m_rectangularSelection = false;
790  if (m_state != PAUSED) {
791  pauseSegmentationMode(true);
792  pauseSegmentationMode(false);
793  }
794 
796  QString(),
797  ecvDisplayTools::UPPER_CENTER_MESSAGE); // clear the area
799  "Segmentation [ON] (polygonal selection)",
803  "Left click: add contour points / Right click: close",
807  ecvDisplayTools::RedrawDisplay(true, false);
808 }
809 
811  if (m_rectangularSelection) return;
812 
813  selectionModelButton->setDefaultAction(actionSetRectangularSelection);
814 
815  m_rectangularSelection = true;
816  if (m_state != PAUSED) {
817  pauseSegmentationMode(true);
818  pauseSegmentationMode(false);
819  }
820 
822  QString(),
823  ecvDisplayTools::UPPER_CENTER_MESSAGE); // clear the area
825  "Segmentation [ON] (rectangular selection)",
829  "Right click: set opposite corners",
833  ecvDisplayTools::RedrawDisplay(true, false);
834 }
835 
838  assert(false);
839  return;
840  }
841 
842  MainWindow* mainWindow = MainWindow::TheInstance();
843  if (mainWindow) {
844  ccHObject* root = mainWindow->dbRootObject();
845  ccHObject::Container polylines;
846  if (root) {
847  root->filterChildren(polylines, true, CV_TYPES::POLY_LINE);
848  }
849 
850  if (!polylines.empty()) {
851  int index = ccItemSelectionDlg::SelectEntity(polylines, 0, this);
852  if (index < 0) return;
853  assert(index >= 0 && index < static_cast<int>(polylines.size()));
854  assert(polylines[index]->isA(CV_TYPES::POLY_LINE));
855  ccPolyline* poly = static_cast<ccPolyline*>(polylines[index]);
856 
857  // look for an associated viewport
858  ccHObject::Container viewports;
859  if (poly->filterChildren(viewports, false,
860  CV_TYPES::VIEWPORT_2D_OBJECT, true) == 1) {
861  // shall we apply this viewport?
862  if (QMessageBox::question(
864  "Associated viewport",
865  "The selected polyline has an associated viewport: "
866  "do you want to apply it?",
867  QMessageBox::Yes,
868  QMessageBox::No) == QMessageBox::Yes) {
870  static_cast<cc2DViewportObject*>(viewports.front())
871  ->getParameters());
874  // m_associatedWin->redraw(false);
875  }
876  }
877 
879  poly->getAssociatedCloud();
880  bool mode3D = !poly->is2DMode();
881 
882  // viewing parameters (for conversion from 3D to 2D)
883  ccGLCameraParameters camera;
885  // const double half_w = camera.viewport[2] / 2.0;
886  // const double half_h = camera.viewport[3] / 2.0;
887 
888  // force polygonal selection mode
892  allowPolylineExport(false);
893 
894  // duplicate polyline 'a minima' (only points and indexes + closed
895  // state)
896  if (m_polyVertices->reserve(vertices->size() +
897  (poly->isClosed() ? 0 : 1)) &&
898  m_segmentationPoly->reserve(poly->size() +
899  (poly->isClosed() ? 0 : 1))) {
900  for (unsigned i = 0; i < vertices->size(); ++i) {
901  CCVector3 P = *vertices->getPoint(i);
902  if (mode3D) {
903  CCVector3d Q2D;
904  camera.project(P, Q2D);
905 
906  P.x = static_cast<PointCoordinateType>(Q2D.x);
907  P.y = static_cast<PointCoordinateType>(Q2D.y);
908  P.z = 0;
909  }
911  }
912  for (unsigned j = 0; j < poly->size(); ++j) {
914  poly->getPointGlobalIndex(j));
915  }
916 
918  if (m_segmentationPoly->isClosed()) {
919  // stop (but we can't all pauseSegmentationMode as it would
920  // remove the current polyline)
921  m_state &= (~RUNNING);
923  } else if (vertices->size()) {
924  // we make as if the segmentation was in progress
925  pauseSegmentationMode(false);
926  unsigned lastIndex = vertices->size() - 1;
928  *m_polyVertices->getPoint(lastIndex));
929  m_segmentationPoly->addPointIndex(lastIndex + 1);
931  m_state |= (POLYLINE | RUNNING);
932  }
933 
934  m_rectangularSelection = false;
936  } else {
937  CVLog::Error("Not enough memory!");
938  }
939  } else {
940  CVLog::Error("No polyline in DB!");
941  }
942  }
943 }
944 
945 static unsigned s_polylineExportCount = 0;
947  MainWindow* mainWindow = MainWindow::TheInstance();
948  if (mainWindow && m_segmentationPoly) {
949  bool mode2D = false;
950  // #ifdef ALLOW_2D_OR_3D_EXPORT
951  QMessageBox messageBox(0);
952  messageBox.setWindowTitle("Choose export type");
953  messageBox.setText(
954  "Export polyline in:\n - 2D (with coordinates relative to the "
955  "screen)\n - 3D (with coordinates relative to the segmented "
956  "entities)");
957  QPushButton* button2D = new QPushButton("2D");
958  QPushButton* button3D = new QPushButton("3D");
959  messageBox.addButton(button2D, QMessageBox::AcceptRole);
960  messageBox.addButton(button3D, QMessageBox::AcceptRole);
961  messageBox.addButton(QMessageBox::Cancel);
962  messageBox.setDefaultButton(button3D);
963  messageBox.exec();
964  if (messageBox.clickedButton() ==
965  messageBox.button(QMessageBox::Cancel)) {
966  // process cancelled by user
967  return;
968  }
969  mode2D = (messageBox.clickedButton() == button2D);
970  // #endif
971 
973 
974  // if the polyline is 2D and we export the polyline in 3D, we must
975  // project its vertices
976  if (!mode2D) {
977  // get current display parameters
978  ccGLCameraParameters camera;
980  const int height = camera.viewport[3];
981 
982  // project the 2D polyline in 3D
984  poly->getAssociatedCloud();
985  ccPointCloud* verticesPC = dynamic_cast<ccPointCloud*>(vertices);
986  if (verticesPC) {
987  for (unsigned i = 0; i < vertices->size(); ++i) {
988  CCVector3* Pscreen =
989  const_cast<CCVector3*>(verticesPC->getPoint(i));
990  CCVector3d Q3D;
992  (int)Pscreen->x, height - (int)Pscreen->y, Q3D);
993  *Pscreen = CCVector3::fromArray(Q3D.u);
994  }
995  verticesPC->invalidateBoundingBox();
996  } else {
997  assert(false);
999  "[Segmentation] Failed to convert 2D polyline to 3D! "
1000  "(internal inconsistency)");
1001  mode2D = false;
1002  }
1003 
1004  // export Global Shift & Scale info (if any)
1005  bool hasGlobalShift = false;
1006  CCVector3d globalShift(0, 0, 0);
1007  double globalScale = 1.0;
1008  {
1009  for (QSet<ccHObject*>::const_iterator it =
1010  m_toSegment.constBegin();
1011  it != m_toSegment.constEnd(); ++it) {
1013  bool isShifted = (shifted && shifted->isShifted());
1014  if (isShifted) {
1015  globalShift = shifted->getGlobalShift();
1016  globalScale = shifted->getGlobalScale();
1017  hasGlobalShift = true;
1018  break;
1019  }
1020  }
1021  }
1022 
1023  if (hasGlobalShift && m_toSegment.size() != 1) {
1024  hasGlobalShift = (QMessageBox::question(
1026  "Apply Global Shift",
1027  "At least one of the segmented "
1028  "entity has been shifted. Apply the "
1029  "same shift to the polyline?",
1030  QMessageBox::Yes,
1031  QMessageBox::No) == QMessageBox::Yes);
1032  }
1033 
1034  if (hasGlobalShift) {
1035  poly->setGlobalShift(globalShift);
1036  poly->setGlobalScale(globalScale);
1037  }
1038  }
1039 
1040  QString polyName = QString("Segmentation polyline #%1")
1041  .arg(++s_polylineExportCount);
1042  poly->setName(polyName);
1043  poly->setEnabled(
1044  false); // we don't want it to appear while the segmentation
1045  // mode is enabled! (anyway it's 2D only...)
1046  poly->set2DMode(mode2D);
1047  poly->setClosed(true);
1048  poly->setColor(ecvColor::yellow); // we use a different color so as to
1049  // differentiate them from the active
1050  // polyline!
1051 
1052  // save associated viewport
1053  cc2DViewportObject* viewportObject =
1054  new cc2DViewportObject(polyName + QString(" viewport"));
1056  // viewportObject->setDisplay(m_associatedWin);
1057  poly->addChild(viewportObject);
1058 
1059  mainWindow->addToDB(poly, false, false, false);
1060  CVLog::Print(QString("[Segmentation] Polyline exported (%1 vertices)")
1061  .arg(poly->size()));
1062  }
1063 }
1064 
1066  m_deleteHiddenParts = false;
1067  stop(true);
1068 }
1069 
1071  m_deleteHiddenParts = true;
1072  stop(true);
1073 }
1074 
1076  reset();
1077  m_deleteHiddenParts = false;
1078  stop(false);
1079 }
1080 
1082  std::set<cc2DLabel*>& watchedLabels,
1083  ccHObject* entity,
1084  const std::vector<int>& newIndexesOfRemainingPointsOrTriangles,
1085  ecvMainAppInterface* app) {
1086  if (!app) {
1087  assert(false);
1088  return;
1089  }
1090 
1091  std::set<cc2DLabel*>::iterator it = watchedLabels.begin();
1092  while (it != watchedLabels.end()) {
1093  cc2DLabel* label = *it;
1094  assert(label);
1095  for (unsigned i = 0; i < label->size(); ++i) {
1096  cc2DLabel::PickedPoint& pp = label->getPickedPoint(i);
1097  if (pp.entity() == entity) {
1098  if (pp.index < newIndexesOfRemainingPointsOrTriangles.size() &&
1099  newIndexesOfRemainingPointsOrTriangles[pp.index] >= 0) {
1100  // update the 'pointer'
1101  pp.index = newIndexesOfRemainingPointsOrTriangles[pp.index];
1102  } else {
1103  // delete the label
1104  ccHObject* labelParent = label->getParent();
1106  bool saveContext = (labelParent != entity &&
1107  !entity->isAncestorOf(labelParent));
1108  if (saveContext)
1109  parentContext = app->removeObjectTemporarilyFromDBTree(
1110  labelParent);
1111  labelParent->removeChild(label);
1112  if (saveContext)
1113  app->putObjectBackIntoDBTree(labelParent,
1114  parentContext);
1115 
1116  label = nullptr;
1117  it = watchedLabels.erase(it);
1118  break;
1119  }
1120  }
1121  }
1122 
1123  if (label) {
1124  // keep the label and move on
1125  ++it;
1126  }
1127  }
1128 }
1129 
1131  ecvMainAppInterface* app, ccHObject::Container& newEntities) {
1132  if (!app) {
1133  assert(false);
1134  return false;
1135  }
1136 
1137  bool cantModifyPolylinesWarningIssued = false;
1138 
1139  // specific case: labels
1140  std::set<cc2DLabel*> watchedLabels;
1141  try {
1142  if (app->dbRootObject()) {
1143  ccHObject::Container loadedLabels;
1144  app->dbRootObject()->filterChildren(loadedLabels, true,
1146 
1147  for (ccHObject* labelEntity : loadedLabels) {
1148  cc2DLabel* label = static_cast<cc2DLabel*>(labelEntity);
1149  if (!label->getParent()) {
1150  // sanity check: should never happen
1151  assert(false);
1152  continue;
1153  }
1154  for (unsigned i = 0; i < label->size(); ++i) {
1155  const cc2DLabel::PickedPoint& pp = label->getPickedPoint(i);
1156  if (m_toSegment.contains(pp.entity())) {
1157  // we will watch this label as it may be deprecated by
1158  // the segmentation process
1159  watchedLabels.insert(label);
1160  break;
1161  }
1162  }
1163  }
1164  }
1165  } catch (const std::bad_alloc&) {
1166  // not enough memory
1167  CVLog::Error(tr("Not enough memory"));
1168  return false;
1169  }
1170 
1171  for (QSet<ccHObject*>::iterator p = m_toSegment.begin();
1172  p != m_toSegment.end();) {
1173  ccHObject* entity = (*p);
1174 
1175  // check first if we can modify this entity directly or if there might
1176  // be dire consequences...
1177  bool canModify = true;
1178  if (entity->isLocked()) {
1179  // we can't delete this entity
1180  CVLog::Warning("Entity " + entity->getName() +
1181  " is locked. We won't be able to modify it");
1182  canModify = false;
1183  }
1184 
1185  if (entity->isKindOf(CV_TYPES::POINT_CLOUD)) {
1186  ccGenericPointCloud* cloud =
1187  static_cast<ccGenericPointCloud*>(entity);
1188  if (cloud->size() == 0) {
1189  // ignore this cloud
1190  CVLog::Warning("Cloud " + cloud->getName() +
1191  " is empty. We will ignore it");
1192  continue;
1193  }
1194  if (canModify) {
1195  // check that the point cloud is not the vertices of a mesh or
1196  // of a polyline
1198  // we can't delete this cloud
1199  CVLog::Warning("Cloud " + cloud->getName() +
1200  " seems to be the vertices of a mesh. We "
1201  "won't be able to modify it");
1202  canModify = false;
1203  } else if (ccPolyline::IsCloudVerticesOfPolyline(cloud)) {
1204  // we can't delete this cloud
1205  CVLog::Warning("Cloud " + cloud->getName() +
1206  " seems to be the vertices of a polyine. We "
1207  "won't be able to modify it");
1208  canModify = false;
1209  }
1210  }
1211  } else if (entity->isA(
1212  CV_TYPES::MESH)) // TODO: sub-meshes and primitives
1213  // are not handled for now
1214  {
1215  ccGenericMesh* mesh = static_cast<ccGenericMesh*>(entity);
1216  if (mesh->size() == 0 || mesh->getAssociatedCloud()->size() == 0) {
1217  // ignore this mesh
1218  CVLog::Warning("Mesh " + mesh->getName() +
1219  " is empty. We will ignore it");
1220  continue;
1221  }
1222  } else if (entity->isKindOf(CV_TYPES::POLY_LINE)) {
1223  ccPolyline* poly = static_cast<ccPolyline*>(entity);
1224  if (poly->size() == 0 || poly->getAssociatedCloud()->size() == 0) {
1225  // ignore this polyline
1226  CVLog::Warning("Polyline " + poly->getName() +
1227  " is empty. We will ignore it");
1228  continue;
1229  }
1230 
1231  // can't modify polylines yet
1232  if (!cantModifyPolylinesWarningIssued) {
1234  "Can't modify polylines. A new polyline will be "
1235  "created.");
1236  cantModifyPolylinesWarningIssued = true;
1237  }
1238  canModify = false;
1239  } else {
1240  // can't change this entity anyway
1241  continue;
1242  }
1243 
1244  if (entity->isKindOf(CV_TYPES::POINT_CLOUD) ||
1245  entity->isKindOf(CV_TYPES::MESH)) {
1246  // we temporarily detach the entity, as it may undergo
1247  // 'severe' modifications (octree deletion, etc.) --> see
1248  // ccPointCloud::createNewCloudFromVisibilitySelection
1250  app->removeObjectTemporarilyFromDBTree(entity);
1251 
1252  bool removeSelectedElementsFromEntity =
1253  (canModify && !m_deleteHiddenParts);
1254 
1255  // apply segmentation
1256  ccHObject* segmentationResult = nullptr;
1257  bool deleteOriginalEntity = (canModify && m_deleteHiddenParts);
1258  if (entity->isKindOf(CV_TYPES::POINT_CLOUD)) {
1259  ccGenericPointCloud* cloud =
1261 
1262  std::vector<int> newIndexesOfRemainingPoints;
1263  ccGenericPointCloud* segmentedCloud =
1265  removeSelectedElementsFromEntity, nullptr,
1266  deleteOriginalEntity
1267  ? nullptr
1268  : &newIndexesOfRemainingPoints);
1269  if (segmentedCloud) {
1270  if (segmentedCloud->size() == 0) {
1271  // empty result: we ignore it
1272  delete segmentedCloud;
1273  segmentedCloud = nullptr;
1274  } else if (segmentedCloud == cloud) {
1275  // specific case: all points were selected, nothing to
1276  // do
1277  app->putObjectBackIntoDBTree(entity, objContext);
1278  ++p;
1279  continue;
1280  } else // we have a new entity
1281  {
1282  segmentationResult = segmentedCloud;
1283 
1284  deleteOriginalEntity |= (cloud->size() == 0);
1285 
1286  if (removeSelectedElementsFromEntity &&
1287  !deleteOriginalEntity) // if we have removed points
1288  // from the original entity
1289  {
1290  // be smart and keep only the necessary labels
1292  watchedLabels, cloud,
1293  newIndexesOfRemainingPoints, app);
1294  }
1295  }
1296  }
1297  } else if (entity->isA(CV_TYPES::MESH)) {
1298  ccMesh* mesh = ccHObjectCaster::ToMesh(entity);
1299 
1300  std::vector<int> newIndexesOfRemainingTriangles;
1301  ccMesh* segmentatedMesh = mesh->createNewMeshFromSelection(
1302  removeSelectedElementsFromEntity,
1303  deleteOriginalEntity ? nullptr
1304  : &newIndexesOfRemainingTriangles,
1305  true);
1306 
1307  if (segmentatedMesh) {
1308  if (segmentatedMesh->size() == 0) {
1309  // empty result: we ignore it
1310  delete segmentatedMesh;
1311  segmentatedMesh = nullptr;
1312  } else if (segmentatedMesh == mesh) {
1313  // specific case: all triangles were selected, nothing
1314  // to do
1315  app->putObjectBackIntoDBTree(entity, objContext);
1316  ++p;
1317  continue;
1318  } else // we have a new entity
1319  {
1320  segmentationResult = segmentatedMesh;
1321 
1322  deleteOriginalEntity |= (mesh->size() == 0);
1323 
1324  if (removeSelectedElementsFromEntity &&
1325  !deleteOriginalEntity) {
1326  // be smart and keep only the necessary labels
1328  watchedLabels, mesh,
1329  newIndexesOfRemainingTriangles, app);
1330  }
1331  }
1332  }
1333  } else {
1334  // we only expect clouds or meshes here
1335  assert(false);
1336  }
1337 
1338  if (segmentationResult) // we have a result (= a new entity)
1339  {
1340  // update suffix
1341  {
1342  QSettings settings;
1343  settings.beginGroup(ccGraphicalSegmentationOptionsDlg::
1344  SegmentationToolOptionsKey());
1345  QString segmentedSuffix =
1346  settings.value(ccGraphicalSegmentationOptionsDlg::
1347  SegmentedSuffixKey(),
1348  ".segmented")
1349  .toString();
1350  settings.endGroup();
1351 
1352  QString resultName = entity->getName();
1353  if (!resultName.endsWith(segmentedSuffix)) {
1354  resultName += segmentedSuffix;
1355  }
1356  segmentationResult->setName(resultName);
1357 
1358  if (segmentationResult->isKindOf(CV_TYPES::MESH) &&
1359  entity->isKindOf(CV_TYPES::MESH)) {
1360  // update the mesh vertices as well
1361  ccGenericMesh* mesh =
1363  ccGenericMesh* resultMesh =
1365  segmentationResult);
1366  QString verticesName =
1367  mesh->getAssociatedCloud()->getName();
1368  if (!verticesName.endsWith(segmentedSuffix)) {
1369  verticesName += segmentedSuffix;
1370  }
1371  resultMesh->getAssociatedCloud()->setName(verticesName);
1372  }
1373  }
1374 
1375  if (removeSelectedElementsFromEntity &&
1376  !deleteOriginalEntity) // if we were able to modify the
1377  // original entity
1378  {
1379  // update the name of the original entity
1380  QSettings settings;
1381  settings.beginGroup(ccGraphicalSegmentationOptionsDlg::
1382  SegmentationToolOptionsKey());
1383  QString remainingSuffix =
1384  settings.value(ccGraphicalSegmentationOptionsDlg::
1385  RemainingSuffixKey(),
1386  ".remaining")
1387  .toString();
1388  settings.endGroup();
1389  if (!entity->getName().endsWith(remainingSuffix)) {
1390  entity->setName(entity->getName() + remainingSuffix);
1391  }
1392  if (entity->isKindOf(CV_TYPES::MESH)) {
1393  // update the mesh vertices as well
1394  ccGenericMesh* mesh =
1396  QString verticesName =
1397  mesh->getAssociatedCloud()->getName();
1398  if (!verticesName.endsWith(remainingSuffix)) {
1399  mesh->getAssociatedCloud()->setName(
1400  verticesName + remainingSuffix);
1401  }
1402  }
1403 
1404  // specific case: deprecate GBL sensors' depth buffer
1405  ccHObject::Container gblSensors;
1406  entity->filterChildren(gblSensors, false,
1408  for (ccHObject* child : gblSensors) {
1409  ccGBLSensor* sensor =
1411  // clear the associated depth buffer of the original
1412  // sensor (deprecated)
1413  sensor->clearDepthBuffer();
1414  assert(entity->isKindOf(CV_TYPES::POINT_CLOUD));
1415  }
1416  }
1417 
1418  // we look for first non-mesh or non-cloud parent
1419  ccHObject* resultParent = objContext.parent;
1420  while (resultParent &&
1421  (resultParent->isKindOf(CV_TYPES::MESH) ||
1422  resultParent->isKindOf(CV_TYPES::POINT_CLOUD))) {
1423  resultParent = resultParent->getParent();
1424  }
1425  if (resultParent) {
1426  resultParent->addChild(segmentationResult);
1427  }
1428 
1429  app->addToDB(segmentationResult, false, true, false, true);
1430 
1431  newEntities.push_back(segmentationResult);
1432  }
1433 
1434  if (!deleteOriginalEntity) {
1435  app->putObjectBackIntoDBTree(entity, objContext);
1436  ++p;
1437  } else {
1438  // remove all labels that depend on this entity
1439  std::set<cc2DLabel*>::iterator it = watchedLabels.begin();
1440  while (it != watchedLabels.end()) {
1441  cc2DLabel* label = *it;
1442  assert(label);
1443  for (unsigned i = 0; i < label->size(); ++i) {
1444  cc2DLabel::PickedPoint& pp = label->getPickedPoint(i);
1445  if (pp.entity() == entity) {
1446  // delete the label
1447  ccHObject* labelParent = label->getParent();
1449  bool saveContext =
1450  (labelParent != entity &&
1451  !entity->isAncestorOf(labelParent));
1452  if (saveContext)
1453  parentContext =
1455  labelParent);
1456  labelParent->removeChild(label);
1457  if (saveContext)
1458  app->putObjectBackIntoDBTree(labelParent,
1459  parentContext);
1460 
1461  label = nullptr;
1462  it = watchedLabels.erase(it);
1463  break;
1464  }
1465  }
1466 
1467  if (label) {
1468  // keep the label and move on
1469  ++it;
1470  }
1471  }
1472 
1473  prepareEntityForRemoval(entity, false);
1474 
1475  p = m_toSegment.erase(p);
1476 
1477  delete entity; // TODO: should we wait that all entities are
1478  // processed before removing it?
1479  entity = nullptr;
1480  }
1481  } else if (entity->isKindOf(CV_TYPES::POLY_LINE)) {
1482  ccPolyline* poly = static_cast<ccPolyline*>(entity);
1483  ccHObject* polyParent = poly->getParent();
1484  if (!polyParent) {
1485  polyParent = app->dbRootObject();
1486  }
1487  assert(polyParent);
1488 
1489  std::vector<ccPolyline*> polylines;
1490  if (poly->createNewPolylinesFromSelection(polylines)) {
1491  for (ccPolyline* p : polylines) {
1492  // p->setDisplay_recursive(poly->getDisplay());
1493  if (polyParent) polyParent->addChild(p);
1494  app->addToDB(p, false, true, false, true);
1495  newEntities.push_back(p);
1496  }
1497  }
1498 
1499  ++p;
1500  } else {
1501  assert(false);
1502  ++p;
1503  }
1504  }
1505 
1507 
1508  return true;
1509 }
constexpr unsigned char POINT_VISIBLE
Definition: CVConst.h:92
constexpr unsigned char POINT_HIDDEN
Definition: CVConst.h:94
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int height
core::Tensor result
Definition: VtkUtils.cpp:76
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
ccHObject * dbRootObject() override
Returns DB root (as a ccHObject)
static MainWindow * TheInstance()
Returns the unique instance of this object.
void addToDB(const QStringList &filenames, QString fileFilter=QString(), bool displayDialog=true)
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
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
unsigned size() const
Returns current size.
Definition: ecv2DLabel.h:74
2D viewport object
const ecvViewportParameters & getParameters() const
Gets parameters.
void setParameters(const ecvViewportParameters &params)
Sets perspective view state.
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showColors(bool state)
Sets colors visibility.
Ground-based Laser sensor.
Definition: ecvGBLSensor.h:26
void clearDepthBuffer()
Removes the associated depth buffer.
Generic mesh interface.
virtual ccGenericPointCloud * getAssociatedCloud() const =0
Returns the vertices cloud.
static bool IsCloudVerticesOfMesh(ccGenericPointCloud *cloud, ccGenericMesh **mesh=nullptr)
Helper to determine if the input cloud acts as vertices of a mesh.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual ccGenericPointCloud * createNewCloudFromVisibilitySelection(bool removeSelectedPoints=false, VisibilityTableType *visTable=nullptr, std::vector< int > *newIndexesOfRemainingPoints=nullptr, bool silent=false, cloudViewer::ReferenceCloud *selection=nullptr)=0
virtual VisibilityTableType & getTheVisibilityArray()
Returns associated visibility array.
virtual bool resetVisibilityArray()
Resets the associated visibility array.
std::vector< unsigned char > VisibilityTableType
Array of "visibility" information for each point.
virtual void unallocateVisibilityArray()
Erases the points visibility information.
virtual bool linkWith(QWidget *win) override
Links the overlay dialog with a MDI window.
void removeAllEntities()
Remove entities from the 'to be segmented' pool.
void allowPolylineExport(bool state)
Whether to allow or not to exort the current segmentation polyline.
bool addEntity(ccHObject *anObject, bool silent=false)
Adds an entity (and/or its children) to the 'to be segmented' pool.
bool applySegmentation(ecvMainAppInterface *app, ccHObject::Container &newEntities)
Apply segmentation and update the database (helper)
void prepareEntityForRemoval(ccHObject *entity, bool unallocateVisibilityArrays)
Prepare entity before removal.
void pauseSegmentationMode(bool state, bool only2D=true)
QSet< ccHObject * > m_toSegment
Set of entities to be segmented.
bool m_somethingHasChanged
Whether something has changed or not (for proper 'cancel')
ccPolyline * m_segmentationPoly
Segmentation polyline.
ccGraphicalSegmentationTool(QWidget *parent)
Default constructor.
unsigned m_state
Current process state.
bool m_deleteHiddenParts
Whether to delete hidden parts after segmentation.
void updatePolyLine(int x, int y, Qt::MouseButtons buttons)
virtual void stop(bool accepted) override
Stops process/dialog.
virtual bool start() override
Starts process.
void onShortcutTriggered(int)
To capture overridden shortcuts (pause button, etc.)
ccPointCloud * m_polyVertices
Segmentation polyline vertices.
static ccMesh * ToMesh(ccHObject *obj)
Converts current object to ccMesh (if possible)
static ccShiftedObject * ToShifted(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccShiftedObject.
static ccPolyline * ToPolyline(ccHObject *obj)
Converts current object to ccPolyline (if possible)
static ccGenericPointCloud * ToGenericPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccGenericPointCloud.
static ccGenericMesh * ToGenericMesh(ccHObject *obj)
Converts current object to ccGenericMesh (if possible)
static ccGBLSensor * ToGBLSensor(ccHObject *obj)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
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.
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
void removeChild(ccHObject *child)
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.
bool pushDisplayState() override
Pushes the current display state (overridden)
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
void popDisplayState(bool apply=true) override
Pops the last pushed display state (overridden)
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
static int SelectEntity(const ccHObject::Container &entities, int defaultSelectedIndex=0, QWidget *parent=0, QString label=QString())
Static shortcut: unique selection mode.
Triangular mesh.
Definition: ecvMesh.h:35
ccMesh * createNewMeshFromSelection(bool removeSelectedTriangles, std::vector< int > *newIndexesOfRemainingTriangles=nullptr, bool withChildEntities=false)
Creates a new mesh with the selected vertices only.
virtual unsigned size() const override
Returns the number of triangles.
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
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 isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
Generic overlay dialog interface.
void shortcutTriggered(int key)
Signal emitted when an overridden key shortcut is pressed.
virtual void stop(bool accepted)
Stops process/dialog.
virtual bool start()
Starts process.
virtual bool linkWith(QWidget *win)
Links the overlay dialog with a MDI window.
void addOverridenShortcut(Qt::Key key)
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void invalidateBoundingBox() override
Invalidates bounding box.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
void clear() override
Clears the entity from all its points and features.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
Colored polyline.
Definition: ecvPolyline.h:24
void setForeground(bool state)
Defines if the polyline is drawn in background or foreground.
virtual void setGlobalShift(const CCVector3d &shift) override
Sets shift applied to original coordinates (information storage only)
bool createNewPolylinesFromSelection(std::vector< ccPolyline * > &output)
Creates a polyline mesh with the selected vertices only.
void set2DMode(bool state)
Defines if the polyline is considered as 2D or 3D.
bool is2DMode() const
Returns whether the polyline is considered as 2D or 3D.
Definition: ecvPolyline.h:63
static bool IsCloudVerticesOfPolyline(ccGenericPointCloud *cloud, ccPolyline **polyline=nullptr)
Helper to determine if the input cloud acts as vertices of a polyline.
virtual void setGlobalScale(double scale) override
void setColor(const ecvColor::Rgb &col)
Sets the polyline color.
Definition: ecvPolyline.h:81
Shifted entity interface.
bool isShifted() const
Returns whether the cloud is shifted or not.
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
virtual unsigned size() const =0
Returns the number of points.
A generic 3D point cloud with index-based and presistent access to points.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
virtual unsigned size() const =0
Returns the number of triangles.
static bool isPointInsidePoly(const CCVector2 &P, const GenericIndexedCloud *polyVertices)
Tests if a point is inside a polygon (2D)
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
const CCVector3 * getPointPersistentPtr(unsigned index) override
unsigned size() const override
Definition: PointCloudTpl.h:38
const CCVector3 * getPoint(unsigned index) const override
void setClosed(bool state)
Sets whether the polyline is closed or not.
Definition: Polyline.h:29
void clear(bool unusedParam=true) override
Clears the cloud.
Definition: Polyline.cpp:15
bool isClosed() const
Returns whether the polyline is closed or not.
Definition: Polyline.h:26
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
virtual GenericIndexedCloudPersist * getAssociatedCloud()
Returns the associated (source) cloud.
unsigned size() const override
Returns the number of points.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
virtual bool resize(unsigned n)
Presets the size of the vector used to store point references.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
static void GetGLCameraParameters(ccGLCameraParameters &params)
Returns the current OpenGL camera parameters.
static bool GetClick3DPos(int x, int y, CCVector3d &P3D)
Returns the approximate 3D position of the clicked pixel.
static const ecvViewportParameters & GetViewportParameters()
static CCVector3d ToVtkCoordinates(int x, int y, int z=0)
static INTERACTION_FLAGS TRANSFORM_CAMERA()
static QMainWindow * GetMainWindow()
static ecvDisplayTools * TheInstance()
static void SetViewportParameters(const ecvViewportParameters &params)
static void SetInteractionMode(INTERACTION_FLAGS flags)
static void DrawWidgets(const WIDGETS_PARAMETER &param, bool update=false)
static QWidget * GetCurrentScreen()
static void SetRedrawRecursive(bool redraw=false)
static void SetPickingMode(PICKING_MODE mode=DEFAULT_PICKING)
static void DisplayNewMessage(const QString &message, MessagePosition pos, bool append=false, int displayMaxDelay_sec=2, MessageType type=CUSTOM_MESSAGE)
Displays a status message in the bottom-left corner.
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
static void RemoveWidgets(const WIDGETS_PARAMETER &param, bool update=false)
Main application interface (for plugins)
virtual ccHObject * dbRootObject()=0
Returns DB root (as a ccHObject)
virtual void putObjectBackIntoDBTree(ccHObject *obj, const ccHObjectContext &context)=0
Adds back object to DB tree.
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual ccHObjectContext removeObjectTemporarilyFromDBTree(ccHObject *obj)=0
Removes object temporarily from DB tree.
@ WIDGET_POLYLINE_2D
static void RemoveUnusedLabelsAndUpdateTheOthers(std::set< cc2DLabel * > &watchedLabels, ccHObject *entity, const std::vector< int > &newIndexesOfRemainingPointsOrTriangles, ecvMainAppInterface *app)
static unsigned s_polylineExportCount
@ HIERARCHY_OBJECT
Definition: CVTypes.h:103
@ VIEWPORT_2D_OBJECT
Definition: CVTypes.h:141
@ PRIMITIVE
Definition: CVTypes.h:119
@ MESH
Definition: CVTypes.h:105
@ GBL_SENSOR
Definition: CVTypes.h:117
@ POINT_CLOUD
Definition: CVTypes.h:104
@ LABEL_2D
Definition: CVTypes.h:140
@ POLY_LINE
Definition: CVTypes.h:112
@ SUB_MESH
Definition: CVTypes.h:106
constexpr Rgb green(0, MAX, 0)
constexpr Rgb yellow(MAX, MAX, 0)
Picked point descriptor.
Definition: ecv2DLabel.h:122
unsigned index
Point/triangle index.
Definition: ecv2DLabel.h:128
ccHObject * entity() const
Returns the associated entity (cloud or mesh)
OpenGL camera parameters.
bool project(const CCVector3d &input3D, CCVector3d &output2D, bool *inFrustum=nullptr) const
Projects a 3D point in 2D (+ normalized 'z' coordinate)
int viewport[4]
Viewport (GL_VIEWPORT)
Backup "context" for an object.