ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvPointListPickingDlg.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 
10 // Qt
11 #include <QApplication>
12 #include <QClipboard>
13 #include <QFileDialog>
14 #include <QMenu>
15 #include <QMessageBox>
16 #include <QSettings>
17 
18 // cloudViewer
19 #include <CVConst.h>
20 #include <CVLog.h>
21 
22 // CV_DB_LIB
23 #include <ecv2DLabel.h>
24 #include <ecvDisplayTools.h>
25 #include <ecvPointCloud.h>
26 #include <ecvPolyline.h>
27 
28 // qCC_io
29 #include <AsciiFilter.h>
30 
31 // local
32 #include "MainWindow.h"
33 #include "db_tree/ecvDBRoot.h"
34 
35 // system
36 #include <assert.h>
37 
38 // semi persistent settings
39 static unsigned s_pickedPointsStartIndex = 0;
41 static const char s_pickedPointContainerName[] = "Picked points list";
42 static const char s_defaultLabelBaseName[] = "Point #";
43 
45  QWidget* parent)
46  : ccPointPickingGenericInterface(pickingHub, parent),
47  Ui::PointListPickingDlg(),
48  m_associatedCloud(0),
49  m_lastPreviousID(0),
50  m_orderedLabelsContainer(0) {
51  setupUi(this);
52 
53  exportToolButton->setPopupMode(QToolButton::MenuButtonPopup);
54  QMenu* menu = new QMenu(exportToolButton);
55  QAction* exportASCII_xyz = menu->addAction("x,y,z");
56  QAction* exportASCII_ixyz = menu->addAction("local index,x,y,z");
57  QAction* exportASCII_gxyz = menu->addAction("global index,x,y,z");
58  QAction* exportASCII_lxyz = menu->addAction("label name,x,y,z");
59  QAction* exportToNewCloud = menu->addAction("new cloud");
60  QAction* exportToNewPolyline = menu->addAction("new polyline");
61  exportToolButton->setMenu(menu);
62 
63  tableWidget->verticalHeader()->setSectionResizeMode(
64  QHeaderView::ResizeToContents);
65 
66  startIndexSpinBox->setValue(s_pickedPointsStartIndex);
67  showGlobalCoordsCheckBox->setChecked(s_showGlobalCoordsCheckBoxChecked);
68 
69  connect(cancelToolButton, &QAbstractButton::clicked, this,
71  connect(revertToolButton, &QAbstractButton::clicked, this,
73  connect(validToolButton, &QAbstractButton::clicked, this,
75  connect(exportToolButton, &QAbstractButton::clicked, exportToolButton,
76  &QToolButton::showMenu);
77  connect(exportASCII_xyz, &QAction::triggered, this,
79  connect(exportASCII_ixyz, &QAction::triggered, this,
81  connect(exportASCII_gxyz, &QAction::triggered, this,
83  connect(exportASCII_lxyz, &QAction::triggered, this,
85  connect(exportToNewCloud, &QAction::triggered, this,
87  connect(exportToNewPolyline, &QAction::triggered, this,
89 
90  connect(markerSizeSpinBox,
91  static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
93  connect(startIndexSpinBox,
94  static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
96 
97  connect(showGlobalCoordsCheckBox, &QAbstractButton::clicked, this,
99 
100  updateList();
101 }
102 
104  std::vector<cc2DLabel*>& pickedPoints) {
105  pickedPoints.clear();
106 
108  // get all labels
109  ccHObject::Container labels;
111  labels, false, CV_TYPES::LABEL_2D);
112 
113  try {
114  pickedPoints.reserve(count);
115  } catch (const std::bad_alloc&) {
116  CVLog::Error("Not enough memory!");
117  return 0;
118  }
119  for (unsigned i = 0; i < count; ++i) {
120  // Warning: cc2DViewportLabel is also a kind of
121  // 'CV_TYPES::LABEL_2D'!
122  if (labels[i]->isA(CV_TYPES::LABEL_2D)) {
123  cc2DLabel* label = static_cast<cc2DLabel*>(labels[i]);
124  if (label->isVisible() && label->size() == 1) {
125  pickedPoints.push_back(label);
126  }
127  }
128  }
129  }
130 
131  return static_cast<unsigned>(pickedPoints.size());
132 }
133 
135  m_associatedCloud = cloud;
136  m_lastPreviousID = 0;
137 
138  if (m_associatedCloud) {
139  // find default container
141  ccHObject::Container groups;
142  m_associatedCloud->filterChildren(groups, true,
144 
145  for (ccHObject::Container::const_iterator it = groups.begin();
146  it != groups.end(); ++it) {
147  if ((*it)->getName() == s_pickedPointContainerName) {
149  break;
150  }
151  }
152 
153  std::vector<cc2DLabel*> previousPickedPoints;
154  unsigned count = getPickedPoints(previousPickedPoints);
155  // find highest unique ID among the VISIBLE labels
156  for (unsigned i = 0; i < count; ++i) {
158  previousPickedPoints[i]->getUniqueID());
159  }
160  }
161 
162  showGlobalCoordsCheckBox->setEnabled(cloud ? cloud->isShifted() : false);
163  updateList();
164 }
165 
167  ccDBRoot* dbRoot = MainWindow::TheInstance()->db();
168  if (!dbRoot) {
169  assert(false);
170  return;
171  }
172 
174  // Restore previous state
175  if (!m_toBeAdded.empty()) {
176  // remove 2D label from rendering window
177  for (size_t j = 0; j < m_toBeAdded.size(); ++j) {
179  if (label) {
180  label->setEnabled(false);
181  label->updateLabel();
182  }
183  }
184 
185  dbRoot->removeElements(m_toBeAdded);
186  }
187 
188  for (size_t j = 0; j < m_toBeDeleted.size(); ++j) {
189  m_toBeDeleted[j]->setEnabled(true);
190  }
191 
195  }
196  }
197 
198  m_toBeDeleted.resize(0);
199  m_toBeAdded.resize(0);
200  m_associatedCloud = 0;
202 
203  updateList();
204 
205  stop(false);
206 }
207 
209  if (!m_associatedCloud) return;
210 
211  // get all labels
212  std::vector<cc2DLabel*> labels;
213  unsigned count = getPickedPoints(labels);
214  if (count != 0) {
215  ccPointCloud* cloud = new ccPointCloud();
216  if (cloud->reserve(count)) {
217  cloud->setName("Picking list");
218  for (unsigned i = 0; i < count; ++i) {
219  const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
220  const CCVector3* P = PP.cloud->getPoint(PP.index);
221  cloud->addPoint(*P);
222  }
223 
224  // cloud->setDisplay(m_associatedCloud->getDisplay());
228  } else {
229  CVLog::Error(
230  "Can't export picked points as point cloud: not enough "
231  "memory!");
232  delete cloud;
233  cloud = 0;
234  }
235  } else {
236  CVLog::Error("Pick some points first!");
237  }
238 }
239 
241  if (!m_associatedCloud) return;
242 
243  // get all labels
244  std::vector<cc2DLabel*> labels;
245  unsigned count = getPickedPoints(labels);
246  if (count > 1) {
247  // we create an "independent" polyline
248  ccPointCloud* vertices = new ccPointCloud("vertices");
249  ccPolyline* polyline = new ccPolyline(vertices);
250 
251  if (!vertices->reserve(count) || !polyline->reserve(count)) {
252  CVLog::Error("Not enough memory!");
253  delete vertices;
254  delete polyline;
255  return;
256  }
257 
258  for (unsigned i = 0; i < count; ++i) {
259  const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
260  vertices->addPoint(*PP.cloud->getPoint(PP.index));
261  }
262  polyline->addPointIndex(0, count);
263  polyline->setVisible(true);
264  vertices->setEnabled(false);
267  polyline->addChild(vertices);
268  MainWindow::TheInstance()->addToDB(polyline);
269  } else {
270  CVLog::Error("Pick at least two points!");
271  }
272 }
273 
275  if (m_associatedCloud && !m_toBeDeleted.empty()) {
276  // apply modifications
277  // no need to redraw as they should already be invisible
279  m_associatedCloud = 0;
280  }
281 
282  m_toBeDeleted.resize(0);
283  m_toBeAdded.resize(0);
285 
286  updateList();
287 
288  stop(true);
289 }
290 
292  if (!m_associatedCloud) return;
293 
294  // get all labels
295  std::vector<cc2DLabel*> labels;
296  unsigned count = getPickedPoints(labels);
297  if (count == 0) return;
298 
299  ccHObject* lastVisibleLabel = labels.back();
300  if (lastVisibleLabel->getUniqueID() <= m_lastPreviousID) {
301  // remove last visible label from rendering window
303  // clear label from rendering window and db tree
304  clearLastLabel(lastVisibleLabel);
305 
306  // old label: hide it and add it to the 'to be deleted' list (will
307  // be restored if process is cancelled)
308  m_toBeDeleted.push_back(lastVisibleLabel);
309  }
310  } else {
311  if (!m_toBeAdded.empty()) {
312  assert(m_toBeAdded.back() == lastVisibleLabel);
313  m_toBeAdded.pop_back();
314  }
315 
317  // clear label from rendering window and db tree
318  clearLastLabel(lastVisibleLabel);
319  } else {
320  m_associatedCloud->detachChild(lastVisibleLabel);
321  }
322  }
323 
324  updateList();
325 }
326 
328  // remove last visible label from rendering window
329  removeEntity(lastVisibleLabel);
330 
331  // remove last visible label from db tree
332  if (lastVisibleLabel->getParent()) {
333  lastVisibleLabel->getParent()->removeDependencyWith(lastVisibleLabel);
334  lastVisibleLabel->removeDependencyWith(lastVisibleLabel->getParent());
335  }
336  MainWindow::TheInstance()->db()->removeElement(lastVisibleLabel);
337 }
338 
340  cc2DLabel* label = ccHObjectCaster::To2DLabel(lastVisibleLabel);
341  if (label) {
342  label->setEnabled(false);
343  label->updateLabel();
344  }
345 }
346 
348  unsigned int uValue = static_cast<unsigned int>(value);
349 
350  if (uValue != s_pickedPointsStartIndex) {
351  s_pickedPointsStartIndex = uValue;
352 
353  updateList();
354  }
355 }
356 
358  if (size < 1) return;
359 
360  // display parameters
362 
363  if (guiParams.labelMarkerSize != static_cast<unsigned>(size)) {
364  guiParams.labelMarkerSize = static_cast<unsigned>(size);
367  }
368 }
369 
371  if (!m_associatedCloud) return;
372 
373  // get all labels
374  std::vector<cc2DLabel*> labels;
375  unsigned count = getPickedPoints(labels);
376  if (count == 0) return;
377 
378  QSettings settings;
379  settings.beginGroup("PointListPickingDlg");
380  QString filename =
381  settings.value("filename", "picking_list.txt").toString();
382  settings.endGroup();
383 
384  filename = QFileDialog::getSaveFileName(this, "Export to ASCII", filename,
386 
387  if (filename.isEmpty()) return;
388 
389  settings.beginGroup("PointListPickingDlg");
390  settings.setValue("filename", filename);
391  settings.endGroup();
392 
393  FILE* fp = fopen(qPrintable(filename), "wt");
394  if (!fp) {
395  CVLog::Error(
396  QString("Failed to open file '%1' for saving!").arg(filename));
397  return;
398  }
399 
400  // if a global shift exists, ask the user if it should be applied
402  double scale = m_associatedCloud->getGlobalScale();
403 
404  if (shift.norm2() != 0 || scale != 1.0) {
405  if (QMessageBox::warning(this, "Apply global shift",
406  "Do you want to apply global shift/scale to "
407  "exported points?",
408  QMessageBox::Yes | QMessageBox::No,
409  QMessageBox::Yes) == QMessageBox::No) {
410  // reset shift
411  shift = CCVector3d(0, 0, 0);
412  scale = 1.0;
413  }
414  }
415 
416  // starting index
417  unsigned startIndex =
418  static_cast<unsigned>(std::max(0, startIndexSpinBox->value()));
419 
420  for (unsigned i = 0; i < count; ++i) {
421  assert(labels[i]->size() == 1);
422  const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
423  const CCVector3* P = PP.cloud->getPoint(PP.index);
424 
425  switch (format) {
427  fprintf(fp, "%u,", i + startIndex);
428  break;
430  fprintf(fp, "%u,", PP.index);
431  break;
433  fprintf(fp, "%s,", qPrintable(labels[i]->getName()));
434  break;
435  default:
436  // nothing to do
437  break;
438  }
439 
440  fprintf(fp, "%.12f,%.12f,%.12f\n",
441  static_cast<double>(P->x) / scale - shift.x,
442  static_cast<double>(P->y) / scale - shift.y,
443  static_cast<double>(P->z) / scale - shift.z);
444  }
445 
446  fclose(fp);
447 
448  CVLog::Print(QString("[I/O] File '%1' saved successfully").arg(filename));
449 }
450 
452  // get all labels
453  std::vector<cc2DLabel*> labels;
454  unsigned count = getPickedPoints(labels);
455 
456  revertToolButton->setEnabled(count);
457  validToolButton->setEnabled(count);
458  exportToolButton->setEnabled(count);
459  countLineEdit->setText(QString::number(count));
460  tableWidget->setRowCount(count);
461 
462  if (!count) return;
463 
464  // starting index
465  int startIndex = startIndexSpinBox->value();
466  int precision = ecvDisplayTools::GetCurrentScreen()
469  : 6;
470 
471  bool showAbsolute = showGlobalCoordsCheckBox->isEnabled() &&
472  showGlobalCoordsCheckBox->isChecked();
473 
474  for (unsigned i = 0; i < count; ++i) {
475  const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
476  const CCVector3* P = PP.cloud->getPoint(PP.index);
477  CCVector3d Pd = (showAbsolute ? PP.cloud->toGlobal3d(*P)
478  : CCVector3d::fromArray(P->u));
479 
480  // point index in list
481  tableWidget->setVerticalHeaderItem(
482  i, new QTableWidgetItem(QString("%1").arg(i + startIndex)));
483  // update name as well
484  // DGM: we don't change the name of old labels that have a non-default
485  // name
486  if (labels[i]->getUniqueID() > m_lastPreviousID ||
487  labels[i]->getName().startsWith(s_defaultLabelBaseName)) {
488  labels[i]->setName(s_defaultLabelBaseName +
489  QString::number(i + startIndex));
490  }
491  // point absolute index (in cloud)
492  tableWidget->setItem(i, 0,
493  new QTableWidgetItem(QString("%1").arg(PP.index)));
494 
495  for (unsigned j = 0; j < 3; ++j)
496  tableWidget->setItem(i, j + 1,
497  new QTableWidgetItem(QString("%1").arg(
498  Pd.u[j], 0, 'f', precision)));
499  }
500 
501  tableWidget->scrollToBottom();
502 }
503 
505  unsigned pointIndex,
506  int x,
507  int y) {
508  if (cloud != m_associatedCloud || !cloud || !MainWindow::TheInstance())
509  return;
510 
511  cc2DLabel* newLabel = new cc2DLabel();
512  newLabel->addPickedPoint(cloud, pointIndex);
513  newLabel->setVisible(true);
514  newLabel->setDisplayedIn2D(false);
515  newLabel->displayPointLegend(true);
516  newLabel->setCollapsed(false);
518  newLabel->setPosition(static_cast<float>(x + 20) / size.width(),
519  static_cast<float>(y + 20) / size.height());
520 
521  // add default container if necessary
526  true, false, false);
527  }
528  assert(m_orderedLabelsContainer);
530  MainWindow::TheInstance()->addToDB(newLabel, false, true, false, false);
531  m_toBeAdded.push_back(newLabel);
532 
533  // automatically send the new point coordinates to the clipboard
534  QClipboard* clipboard = QApplication::clipboard();
535  if (clipboard) {
536  const CCVector3* P = cloud->getPoint(pointIndex);
537  int precision = ecvDisplayTools::GetCurrentScreen()
540  : 6;
541  int indexInList =
542  startIndexSpinBox->value() +
543  static_cast<int>(
545  1;
546  clipboard->setText(QString("CC_POINT_#%0(%1;%2;%3)")
547  .arg(indexInList)
548  .arg(P->x, 0, 'f', precision)
549  .arg(P->y, 0, 'f', precision)
550  .arg(P->z, 0, 'f', precision));
551  }
552 
553  updateList();
554 
555  if (newLabel) {
556  newLabel->updateLabel();
557  }
558 }
Vector3Tpl< double > CCVector3d
Double 3D Vector.
Definition: CVGeom.h:804
std::string filename
filament::Texture::InternalFormat format
int size
int count
static QString GetFileFilter()
Definition: AsciiFilter.h:26
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
static MainWindow * TheInstance()
Returns the unique instance of this object.
ccDBRoot * db()
Returns real 'dbRoot' object.
Definition: MainWindow.h:193
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
Type norm2() const
Returns vector square norm.
Definition: CVGeom.h:417
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
bool addPickedPoint(ccGenericPointCloud *cloud, unsigned pointIndex, bool entityCenter=false)
Adds a point to this label.
void updateLabel()
void setCollapsed(bool state)
Whether to collapse label or not.
Definition: ecv2DLabel.h:102
void setDisplayedIn2D(bool state)
Whether to display the label in 2D.
Definition: ecv2DLabel.h:114
unsigned size() const
Returns current size.
Definition: ecv2DLabel.h:74
void setPosition(float x, float y)
Sets relative position.
void displayPointLegend(bool state)
Whether to display the point(s) legend (title only)
Definition: ecv2DLabel.h:108
GUI database tree root.
Definition: ecvDBRoot.h:65
void removeElements(ccHObject::Container &objects)
Removes several elements at once from the DB tree.
Definition: ecvDBRoot.cpp:513
void removeElement(ccHObject *object)
Removes an element from the DB tree.
Definition: ecvDBRoot.cpp:554
virtual bool isVisible() const
Returns whether entity is visible or not.
virtual void setVisible(bool state)
Sets entity visibility.
static cc2DLabel * To2DLabel(ccHObject *obj)
Converts current object to cc2DLabel (if possible)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void removeDependencyWith(ccHObject *otherObject)
Removes any dependency flags with a given object.
void detachChild(ccHObject *child)
Detaches a specific child.
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
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.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
virtual unsigned getUniqueID() const
Returns object unique ID.
Definition: ecvObject.h:86
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
Point/triangle picking hub.
Definition: ecvPickingHub.h:29
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
void exportToASCII_ixyz()
Exports list to an 'ixyz' ASCII file.
void exportToNewPolyline()
Exports list to a polyline.
void exportToASCII_gxyz()
Exports list to an 'gxyz' ASCII file.
void linkWithCloud(ccPointCloud *cloud)
Associates dialog with cloud.
void removeLastEntry()
Removes last inserted point from list.
void exportToASCII(ExportFormat format)
Exports list to an ASCII file.
void removeEntity(ccHObject *lastVisibleLabel)
unsigned m_lastPreviousID
Last existing label unique ID on load.
ccHObject::Container m_toBeDeleted
ccHObject * m_orderedLabelsContainer
Ordered labels container.
void startIndexChanged(int)
Redraw window when starting index changes.
void exportToASCII_lxyz()
Exports list to an 'lxyz' ASCII file.
ccPointCloud * m_associatedCloud
Associated cloud.
void updateList()
Updates point list widget.
ccPointListPickingDlg(ccPickingHub *pickingHub, QWidget *parent)
Default constructor.
void clearLastLabel(ccHObject *lastVisibleLabel)
void exportToASCII_xyz()
Exports list to an 'xyz' ASCII file.
virtual void processPickedPoint(ccPointCloud *cloud, unsigned pointIndex, int x, int y) override
Generic method to process picked points.
unsigned getPickedPoints(std::vector< cc2DLabel * > &pickedPoints)
Gets current (visible) picked points from the associated cloud.
void markerSizeChanged(int)
Redraw window when marker size changes.
void exportToNewCloud()
Exports list to a new cloud.
void applyAndExit()
Applies changes and exit.
ccHObject::Container m_toBeAdded
void cancelAndExit()
Cancels process and exit.
void stop(bool state) override
Stops process/dialog.
Colored polyline.
Definition: ecvPolyline.h:24
virtual void setGlobalShift(const CCVector3d &shift) override
Sets shift applied to original coordinates (information storage only)
virtual void setGlobalScale(double scale) override
virtual void setGlobalScale(double scale)
CCVector3d toGlobal3d(const Vector3Tpl< T > &Plocal) const
Returns the point back-projected into the original coordinates system.
bool isShifted() const
Returns whether the cloud is shifted or not.
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
const CCVector3 * getPoint(unsigned index) const override
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
static const ecvGui::ParamStruct & GetDisplayParameters()
Returns current parameters for this display (const version)
static void SetDisplayParameters(const ecvGui::ParamStruct &params)
Sets current parameters for this display.
static QSize GetScreenSize()
static QWidget * GetCurrentScreen()
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
int max(int a, int b)
Definition: cutil_math.h:48
static unsigned s_pickedPointsStartIndex
static bool s_showGlobalCoordsCheckBoxChecked
static const char s_defaultLabelBaseName[]
static const char s_pickedPointContainerName[]
@ HIERARCHY_OBJECT
Definition: CVTypes.h:103
@ LABEL_2D
Definition: CVTypes.h:140
Picked point descriptor.
Definition: ecv2DLabel.h:122
unsigned index
Point/triangle index.
Definition: ecv2DLabel.h:128
ccGenericPointCloud * cloud
Cloud.
Definition: ecv2DLabel.h:124
GUI parameters.
unsigned labelMarkerSize
Label marker size.
unsigned displayedNumPrecision
Displayed numbers precision.