ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvTracePolylineTool.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 "ecvTracePolylineTool.h"
9 
10 // Local
11 #include "MainWindow.h"
12 
13 // common
14 #include <ecvPickingHub.h>
15 
16 // CV_CORE_LIB
17 #include <CVLog.h>
19 #include <SquareMatrix.h>
20 
21 // CV_DB_LIB
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 // Qt
31 #include <QApplication>
32 #include <QMenu>
33 #include <QMessageBox>
34 #include <QProgressDialog>
35 #include <QPushButton>
36 
37 // System
38 #include <assert.h>
39 
40 #include <iostream>
41 
46  clickPos = CCVector2d(pos2D.x, pos2D.y);
47  }
48 }
49 
51  QWidget* parent)
52  : ccOverlayDialog(parent),
53  Ui::TracePolyLineDlg(),
54  m_polyTip(nullptr),
55  m_polyTipVertices(nullptr),
56  m_poly3D(nullptr),
57  m_poly3DVertices(nullptr),
58  m_done(false),
59  m_pickingHub(pickingHub) {
60  assert(pickingHub);
61 
62  setupUi(this);
63  setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
64 
65  connect(saveToolButton, &QToolButton::clicked, this,
67  connect(resetToolButton, &QToolButton::clicked, this,
69  connect(continueToolButton, &QToolButton::clicked, this,
71  connect(validButton, &QToolButton::clicked, this,
73  connect(cancelButton, &QToolButton::clicked, this,
75  connect(widthSpinBox,
76  static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
78 
79  // add shortcuts
80  addOverridenShortcut(Qt::Key_Escape); // escape key for the "cancel" button
81  addOverridenShortcut(Qt::Key_Return); // return key for the "apply" button
82  connect(this, &ccTracePolylineTool::shortcutTriggered, this,
84 
85  m_polyTipVertices = new ccPointCloud("Tip vertices");
90 
92  m_polyTip->setForeground(true);
94  m_polyTip->set2DMode(true);
95  m_polyTip->reserve(2);
96  m_polyTip->addPointIndex(0, 2);
98  widthSpinBox->value() < 2
99  ? 0
100  : widthSpinBox->value()); //'1' is equivalent to the
101  // default line size
103 
104  validButton->setEnabled(false);
105 }
106 
108  if (m_polyTip) delete m_polyTip;
109  // DGM: already a child of m_polyTip
110  // if (m_polyTipVertices)
111  // delete m_polyTipVertices;
112 
113  if (m_poly3D) delete m_poly3D;
114  // DGM: already a child of m_poly3D
115  // if (m_poly3DVertices)
116  // delete m_poly3DVertices;
117 }
118 
120  switch (key) {
121  case Qt::Key_Return:
122  apply();
123  return;
124 
125  case Qt::Key_Escape:
126  cancel();
127  return;
128 
129  default:
130  // nothing to do
131  break;
132  }
133 }
134 
136  if (!m_poly3D || !m_poly3DVertices ||
137  m_segmentParams.size() != m_poly3DVertices->size()) {
138  assert(false);
139  return nullptr;
140  }
141 
142  if (steps <= 1) {
143  // nothing to do
144  return nullptr;
145  }
146 
147  ccHObject::Container clouds;
149  CV_TYPES::POINT_CLOUD, false);
150  ccHObject::Container meshes;
152  false);
153 
154  if (clouds.empty() && meshes.empty()) {
155  // no entity is currently displayed?!
156  assert(false);
157  return nullptr;
158  }
159 
160  unsigned n_verts = m_poly3DVertices->size();
161  unsigned n_segments = m_poly3D->size() - (m_poly3D->isClosed() ? 0 : 1);
162  unsigned end_size = n_segments * steps + (m_poly3D->isClosed() ? 0 : 1);
163 
164  ccPointCloud* newVertices = new ccPointCloud();
165  ccPolyline* newPoly = new ccPolyline(newVertices);
166  newPoly->addChild(newVertices);
167 
168  if (!newVertices->reserve(end_size) || !newPoly->reserve(end_size)) {
170  "[ccTracePolylineTool::PolylineOverSampling] Not enough "
171  "memory");
172  delete newPoly;
173  return nullptr;
174  }
176  newVertices->setName(m_poly3DVertices->getName());
177  newVertices->setEnabled(m_poly3DVertices->isEnabled());
178  newPoly->importParametersFrom(*m_poly3D);
179 
180  QProgressDialog pDlg(QString("Oversampling"), "Cancel", 0,
181  static_cast<int>(end_size),
184  : 0);
185  pDlg.show();
186  QCoreApplication::processEvents();
187 
188  for (unsigned i = 0; i < n_segments; ++i) {
189  const CCVector3* p1 = m_poly3DVertices->getPoint(i);
190  newVertices->addPoint(*p1);
191 
192  unsigned i2 = (i + 1) % n_verts;
193  CCVector2d v =
194  m_segmentParams[i2].clickPos - m_segmentParams[i].clickPos;
195  v /= steps;
196 
197  for (unsigned j = 1; j < steps; j++) {
198  CCVector2d vj = m_segmentParams[i].clickPos + v * j;
199 
200  CCVector3 nearestPoint;
201  double nearestElementSquareDist = -1.0;
202 
203  // for each cloud
204  for (size_t c = 0; c < clouds.size(); ++c) {
205  ccGenericPointCloud* cloud =
206  static_cast<ccGenericPointCloud*>(clouds[c]);
207 
208  int nearestPointIndex = -1;
209  double nearestSquareDist = 0;
210  if (cloud->pointPicking(vj, m_segmentParams[i2].params,
211  nearestPointIndex, nearestSquareDist,
212  snapSizeSpinBox->value(),
213  snapSizeSpinBox->value(), true)) {
214  if (nearestElementSquareDist < 0 ||
215  nearestSquareDist < nearestElementSquareDist) {
216  nearestElementSquareDist = nearestSquareDist;
217  nearestPoint = *cloud->getPoint(nearestPointIndex);
218  }
219  }
220  }
221 
222  // for each mesh
223  for (size_t m = 0; m < meshes.size(); ++m) {
224  ccGenericMesh* mesh = static_cast<ccGenericMesh*>(meshes[m]);
225  int nearestTriIndex = -1;
226  double nearestSquareDist = 0;
227  CCVector3d _nearestPoint;
228 
229  if (mesh->trianglePicking(vj, m_segmentParams[i2].params,
230  nearestTriIndex, nearestSquareDist,
231  _nearestPoint)) {
232  if (nearestElementSquareDist < 0 ||
233  nearestSquareDist < nearestElementSquareDist) {
234  nearestElementSquareDist = nearestSquareDist;
235  nearestPoint = CCVector3::fromArray(_nearestPoint.u);
236  }
237  }
238  }
239 
240  if (nearestElementSquareDist >= 0) {
241  newVertices->addPoint(nearestPoint);
242  }
243 
244  if (pDlg.wasCanceled()) {
245  steps = 0; // quick finish ;)
246  break;
247  }
248  pDlg.setValue(pDlg.value() + 1);
249  }
250  }
251 
252  // add last point
253  if (!m_poly3D->isClosed()) {
254  newVertices->addPoint(*m_poly3DVertices->getPoint(n_verts - 1));
255  }
256 
257  newVertices->shrinkToFit();
258  newPoly->addPointIndex(0, newVertices->size());
259 
260  return newPoly;
261 }
262 
263 bool ccTracePolylineTool::linkWith(QWidget* win) {
264  assert(m_polyTip);
265  assert(!m_poly3D);
266 
267  if (!ccOverlayDialog::linkWith(win)) {
268  return false;
269  }
270 
277  }
278 
279  return true;
280 }
281 
282 static int s_defaultPickingRadius = 1;
283 static int s_overSamplingCount = 1;
285  assert(m_polyTip);
286  assert(!m_poly3D);
287 
289  CVLog::Warning("[Trace Polyline Tool] No associated window!");
290  return false;
291  }
292 
293  if (m_pickingHub) {
295  }
302  ecvDisplayTools::GetCurrentScreen()->setCursor(Qt::CrossCursor);
303 
304  snapSizeSpinBox->blockSignals(true);
305  snapSizeSpinBox->setValue(s_defaultPickingRadius);
306  snapSizeSpinBox->blockSignals(false);
307 
308  oversampleSpinBox->blockSignals(true);
309  oversampleSpinBox->setValue(s_overSamplingCount);
310  oversampleSpinBox->blockSignals(false);
311 
312  resetLine(); // to reset the GUI
313 
314  return ccOverlayDialog::start();
315 }
316 
317 void ccTracePolylineTool::stop(bool accepted) {
318  assert(m_polyTip);
319 
320  if (m_pickingHub) {
322  }
323 
326  "Polyline tracing [OFF]", ecvDisplayTools::UPPER_CENTER_MESSAGE,
328 
329  resetTip();
332  ecvDisplayTools::GetCurrentScreen()->setCursor(Qt::ArrowCursor);
333  }
334 
335  s_defaultPickingRadius = snapSizeSpinBox->value();
336  s_overSamplingCount = oversampleSpinBox->value();
337 
338  ccOverlayDialog::stop(accepted);
339 }
340 
342  int y,
343  Qt::MouseButtons buttons) {
345  assert(false);
346  return;
347  }
348 
349  if (buttons != Qt::NoButton) {
350  // nothing to do (just hide the tip)
351  if (m_polyTip->isEnabled()) {
352  m_polyTip->setEnabled(false);
353  updateTip();
354  }
355  return;
356  }
357 
358  if (!m_poly3DVertices || m_poly3DVertices->size() == 0) {
359  // there should be at least one point already picked!
360  return;
361  }
362 
363  if (m_done) {
364  // when it is done do nothing
365  return;
366  }
367 
368  assert(m_polyTip && m_polyTipVertices && m_polyTipVertices->size() == 2);
369 
370  // we replace the last point by the new one
371  {
373  CCVector3 P2D(static_cast<PointCoordinateType>(pos2D.x),
374  static_cast<PointCoordinateType>(pos2D.y), 0);
375 
376  CCVector3* lastP = const_cast<CCVector3*>(
378  *lastP = P2D;
379  }
380 
381  // just in case (e.g. if the view has been rotated or zoomed)
382  // we also update the first vertex position!
383  {
384  const CCVector3* P3D =
386 
387  ccGLCameraParameters camera;
389 
390  CCVector3d A2D;
391  camera.project(*P3D, A2D);
392 
393  CCVector3* firstP = const_cast<CCVector3*>(
395  *firstP = CCVector3(
396  static_cast<PointCoordinateType>(
397  A2D.x), // we convert A2D to centered coordinates (no
398  // need to apply high DPI scale or anything!)
399  static_cast<PointCoordinateType>(A2D.y), 0);
400  }
401 
402  m_polyTip->setEnabled(true);
403 
404  updateTip();
405 }
406 
409  assert(false);
410  return;
411  }
412 
413  if (!pi.entity) {
414  // means that the mouse has been clicked but no point was found!
415  return;
416  } else {
417  // avoid rendering this entity again.
418  pi.entity->setRedraw(false);
419  }
420 
421  // if the 3D polyline doesn't exist yet, we create it
422  if (!m_poly3D || !m_poly3DVertices) {
423  m_poly3DVertices = new ccPointCloud("Vertices");
425 
428  m_poly3D->set2DMode(false);
431  widthSpinBox->value() < 2
432  ? 0
433  : widthSpinBox->value()); //'1' is equivalent to the
434  // default line size
435 
436  ccGenericPointCloud* cloud =
438  if (cloud) {
439  // copy the first clicked entity's global shift & scale
442  }
443 
444  m_segmentParams.resize(0); // just in case
445 
446  // ecvDisplayTools::AddToOwnDB(m_poly3D);
447  }
448 
449  // try to add one more point
452  CVLog::Error("Not enough memory");
453  return;
454  }
455 
456  try {
457  m_segmentParams.reserve(m_segmentParams.size() + 1);
458  } catch (const std::bad_alloc&) {
459  CVLog::Error("Not enough memory");
460  return;
461  }
462 
465  m_segmentParams.emplace_back(pi.clickPoint.x(), pi.clickPoint.y());
466 
467  // we replace the first point of the tip by this new point
468  {
470  pi.clickPoint.y());
471  CCVector3 P2D(static_cast<PointCoordinateType>(pos2D.x),
472  static_cast<PointCoordinateType>(pos2D.y), 0);
473 
474  CCVector3* firstTipPoint = const_cast<CCVector3*>(
476  *firstTipPoint = P2D;
477  m_polyTip->setEnabled(false); // don't need to display it for now
478  }
479 
480  updatePoly3D();
481 }
482 
484  // CTRL + right click = panning
485  if (!m_poly3D ||
486  (QApplication::keyboardModifiers() & Qt::ControlModifier)) {
487  return;
488  }
489 
490  unsigned vertCount = m_poly3D->size();
491  if (vertCount < 2) {
492  // discard this polyline
493  resetLine();
494  } else {
495  // hide the tip
496  if (m_polyTip) {
497  m_polyTip->setEnabled(false);
498  }
499  // update the GUI
500  validButton->setEnabled(true);
501  saveToolButton->setEnabled(true);
502  resetToolButton->setEnabled(true);
503  continueToolButton->setEnabled(true);
504  if (m_pickingHub) {
506  }
508  ecvDisplayTools::NO_PICKING); // no more picking
509  m_done = true;
510 
511  updateTip();
512  }
513 }
514 
516  if (m_poly3D) {
517  if (reset) {
518  // discard this polyline
519  resetPoly3D();
520 
521  if (m_polyTip) {
522  // hide the tip
523  m_polyTip->setEnabled(false);
524  }
525 
526  delete m_poly3D;
527  m_segmentParams.resize(0);
528  // delete m_poly3DVertices;
529  m_poly3D = nullptr;
530  m_poly3DVertices = nullptr;
531  } else {
532  if (m_polyTip) {
533  // show the tip
534  m_polyTip->setEnabled(true);
535  }
536  }
537  }
538 
539  // enable picking
540  if (m_pickingHub &&
542  this, true /*, true, ecvDisplayTools::POINT_PICKING*/)) {
543  CVLog::Error(
544  "The picking mechanism is already in use. Close the tool using "
545  "it first.");
546  }
547 
548  if (m_polyTip) {
549  updateTip();
550  }
551 
552  validButton->setEnabled(false);
553  saveToolButton->setEnabled(false);
554  resetToolButton->setEnabled(false);
555  continueToolButton->setEnabled(false);
556  m_done = false;
557 }
558 
560  if (!m_poly3D) {
561  return;
562  }
563 
564  unsigned overSampling = static_cast<unsigned>(oversampleSpinBox->value());
565  if (overSampling > 1) {
566  ccPolyline* poly = polylineOverSampling(overSampling);
567  if (poly) {
568  resetPoly3D();
569 
570  delete m_poly3D;
571  m_segmentParams.resize(0);
572  m_poly3DVertices = nullptr;
573  m_poly3D = poly;
574  }
575  }
576 
579  if (MainWindow::TheInstance()) {
581  } else {
582  assert(false);
583  }
584 
585  m_poly3D = nullptr;
586  m_segmentParams.resize(0);
587  m_poly3DVertices = nullptr;
588 
589  resetLine(); // to update the GUI
590 }
591 
593  exportLine();
594  stop(true);
595 }
596 
598  resetLine();
599 
600  stop(false);
601 }
602 
604  if (m_poly3D) {
606  }
607  if (m_polyTip) {
609  }
610 
612  if (m_poly3D) {
613  updatePoly3D();
614  }
615  if (m_polyTip) {
616  updateTip();
617  }
618  }
619 }
620 
621 // widget method
626  m_poly3D->getViewId()),
627  true);
628  }
629 }
630 
635  true);
636  }
637 }
638 
643  m_polyTip->getViewId()),
644  true);
645  }
646 }
647 
652  true);
653  }
654 }
Vector2Tpl< double > CCVector2d
Double 2D Vector.
Definition: CVGeom.h:783
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 width
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
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.
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
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
virtual void setTempColor(const ecvColor::Rgb &col, bool autoActivate=true)
Sets current temporary (unique)
virtual void setRedraw(bool state)
Sets entity redraw mode.
Generic mesh interface.
virtual bool trianglePicking(const CCVector2d &clickPos, const ccGLCameraParameters &camera, int &nearestTriIndex, double &nearestSquareDist, CCVector3d &nearestPoint, CCVector3d *barycentricCoords=nullptr) const
Brute force triangle picking.
A 3D cloud interface with associated features (color, normals, octree, etc.)
bool pointPicking(const CCVector2d &clickPos, const ccGLCameraParameters &camera, int &nearestPointIndex, double &nearestSquareDist, double pickWidth=2.0, double pickHeight=2.0, bool autoComputeOctree=false)
Point picking (brute force or octree-driven)
static ccGenericPointCloud * ToGenericPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccGenericPointCloud.
QString getViewId() const
Definition: ecvHObject.h:225
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a 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.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
virtual bool isEnabled() const
Returns whether the object is enabled or not.
Definition: ecvObject.h:97
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)
Point/triangle picking hub.
Definition: ecvPickingHub.h:29
void removeListener(ccPickingListener *listener, bool autoStopPickingIfLast=true)
Removes a listener.
bool addListener(ccPickingListener *listener, bool exclusive=false, bool autoStartPicking=true, ecvDisplayTools::PICKING_MODE mode=ecvDisplayTools::POINT_OR_TRIANGLE_PICKING)
Adds a listener.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
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)
void set2DMode(bool state)
Defines if the polyline is considered as 2D or 3D.
void importParametersFrom(const ccPolyline &poly)
Copy the parameters from another polyline.
virtual void setGlobalScale(double scale) override
void setColor(const ecvColor::Rgb &col)
Sets the polyline color.
Definition: ecvPolyline.h:81
void setWidth(PointCoordinateType width)
Sets the width of the line.
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
ccPointCloud * m_poly3DVertices
3D polyline vertices
ccPolyline * m_poly3D
3D polyline
void restart(bool reset)
Restarts the edition mode.
void onShortcutTriggered(int)
To capture overridden shortcuts (pause button, etc.)
virtual void onItemPicked(const PickedItem &pi) override
Inherited from ccPickingListener.
ccPolyline * polylineOverSampling(unsigned steps) const
Oversamples the active 3D polyline.
virtual ~ccTracePolylineTool()
Destructor.
void updatePolyLineTip(int x, int y, Qt::MouseButtons buttons)
bool m_done
Current process state.
ccTracePolylineTool(ccPickingHub *pickingHub, QWidget *parent)
Default constructor.
ccPointCloud * m_polyTipVertices
2D polyline vertices
virtual void stop(bool accepted) override
Stops process/dialog.
ccPolyline * m_polyTip
2D polyline (for the currently edited part)
std::vector< SegmentGLParams > m_segmentParams
Viewport parameters use to draw each segment of the polyline.
ccPickingHub * m_pickingHub
Picking hub.
void closePolyLine(int x=0, int y=0)
virtual bool linkWith(QWidget *win) override
Links the overlay dialog with a MDI window.
virtual bool start() override
Starts process.
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 * getPointPersistentPtr(unsigned index) override
unsigned size() const override
Definition: PointCloudTpl.h:38
const CCVector3 * getPoint(unsigned index) const override
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.
unsigned size() const override
Returns the number of points.
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 CCVector3d ToVtkCoordinates(int x, int y, int z=0)
static INTERACTION_FLAGS TRANSFORM_CAMERA()
static ccHObject * GetSceneDB()
void mouseMoved(int x, int y, Qt::MouseButtons buttons)
Signal emitted when the mouse is moved.
static ecvDisplayTools * TheInstance()
static void SetInteractionMode(INTERACTION_FLAGS flags)
static void DrawWidgets(const WIDGETS_PARAMETER &param, bool update=false)
static QWidget * GetCurrentScreen()
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.
void rightButtonClicked(int x, int y)
Signal emitted when the right mouse button is cliked on the window.
static void RemoveWidgets(const WIDGETS_PARAMETER &param, bool update=false)
@ WIDGET_POLYLINE
static int s_defaultPickingRadius
static int s_overSamplingCount
@ MESH
Definition: CVTypes.h:105
@ POINT_CLOUD
Definition: CVTypes.h:104
constexpr Rgb green(0, MAX, 0)
OpenGL camera parameters.
bool project(const CCVector3d &input3D, CCVector3d &output2D, bool *inFrustum=nullptr) const
Projects a 3D point in 2D (+ normalized 'z' coordinate)