ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ccTraceTool.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 "ccTraceTool.h"
9 
10 #include <QApplication>
11 #include <QMainWindow>
12 #include <QMessageBox>
13 
14 #include "ccCompass.h"
15 
17 
19 
20 // called when the tool is set to active (for initialization)
22  // try "pick-up" selected trace
24 }
25 
27  accept(); // accept any changes
28 }
29 
30 // called when a point in a point cloud gets picked while this tool is active
32  unsigned itemIdx,
33  ccPointCloud* cloud,
34  const CCVector3& P) {
35  // try and fetch the trace object (returns null if the id is invalid)
36  ccTrace* t =
37  dynamic_cast<ccTrace*>(m_app->dbRootObject()->find(m_trace_id));
38 
39  m_changed = true; // trace has been modified
40 
41  // no active trace -> make a new one
42  if (!t) {
43  t = new ccTrace(cloud);
44  // t->setDisplay(m_window);
45  t->setVisible(true);
46  t->setName("Trace");
47  // t->prepareDisplayForRefresh_recursive();
48  m_trace_id = t->getUniqueID();
49  insertPoint->addChild(t);
50  m_app->addToDB(t, false, false, false, false);
51  t->setActive(true);
52  m_app->setSelectedInDB(t, true);
53  m_preExisting = false;
54  }
55 
56  // if cost function is gradient/curvature then check appropriate SFs have
57  // been defined
58  if (ccTrace::COST_MODE & ccTrace::GRADIENT) // gradient cost is active
59  {
61  !t->isGradientPrecomputed()) // not already computed
62  {
63  // give user a chance to bail - computations can take looooong
64  QMessageBox::StandardButton q;
65  q = QMessageBox::question(
66  m_app->getMainWindow(), "Calculate gradient?",
67  "Precompute Gradient? This can be slow, but once complete "
68  "will greatly decrease future computation times.",
69  QMessageBox::Yes | QMessageBox::No);
70  if (q == QMessageBox::Yes) // do compute
71  {
73  } else {
74  m_precompute_gradient = false; // only need to do this once
75  }
76  }
77  }
78  if (ccTrace::COST_MODE & ccTrace::CURVE) // curvature cost is active
79  {
81  !t->isCurvaturePrecomputed()) // not already computed?
82  {
83  QMessageBox::StandardButton q;
84  q = QMessageBox::question(
85  m_app->getMainWindow(), "Calculate curvature?",
86  "Precompute Curvature? This can be slow, but once complete "
87  "will greatly decrease future computation times.",
88  QMessageBox::Yes | QMessageBox::No);
89  if (q == QMessageBox::Yes) // do compute
90  {
92  } else {
93  m_precompute_curvature = false; // only need to do once
94  }
95  }
96  }
97 
98  // add point
99  int index = t->insertWaypoint(itemIdx);
100 
101  // optimise points
102  if (t->waypoint_count() >= 2) {
103  if (!t->optimizePath()) // optimize the path!
104  {
105  //... problem?
106  m_app->dispToConsole(QString("[ccCompass] Failed to optimize trace "
107  "path... please try again."),
109  t->undoLast(); // go back a step
110 
111  if (t->size() < 2) // degenerate trace - delete
112  {
113  m_app->removeFromDB(t);
114  m_trace_id = -1; // start from scratch next time
115  }
116  }
117  }
118 }
119 
120 // called when "Return" or "Space" is pressed, or the "Accept Button" is clicked
121 // or the tool is disactivated
123  // finish trace
125 }
126 
127 // called when the "Escape" is pressed, or the "Cancel" button is clicked
129  ccTrace* t =
130  dynamic_cast<ccTrace*>(m_app->dbRootObject()->find(m_trace_id));
131 
132  if (t) {
133  t->setActive(false); // disactivate trace
134 
135  if (!m_preExisting) // delete new traces (i.e. that were "picked-up" by
136  // changing the selection)
137  {
138  m_app->removeFromDB(t); // delete trace object
139  m_trace_id = -1;
140  }
141  }
142 }
143 
144 void ccTraceTool::onNewSelection(const ccHObject::Container& selectedEntities) {
145  // can we pick up a new trace?
146  if (selectedEntities.size() > 0) // non-empty selection
147  {
148  // selection is the already active trace?
149  if (selectedEntities[0]->getUniqueID() == m_trace_id) {
150  return; // we're already on it
151  } else if (pickupTrace(
152  selectedEntities[0])) // try pick-up the selection
153  {
154  return; // bail - we've found our new active trace object
155  }
156 
157  // new selection is not a trace - finish the last one
159  }
160 }
161 
163  // find current trace [if there is one]
164  ccTrace* t =
165  dynamic_cast<ccTrace*>(m_app->dbRootObject()->find(m_trace_id));
166 
167  if (t) {
168  t->setActive(false);
169  t->finalizePath();
170 
171  // check for shift key modifier (flips the fitPlane modifier)
172  bool fitPlane = ccCompass::fitPlanes;
173  bool shift = QApplication::keyboardModifiers().testFlag(
174  Qt::ShiftModifier); // shift is pressed
175  if (shift) {
176  fitPlane = !fitPlane;
178  false; // probably want different outcome to before
179  m_childPlaneDeleted = false;
180  m_changed = true;
181  }
182 
183  // fit plane
184  if ((fitPlane && m_changed) ||
186  // calculate fit plane
187  ccPlane* p = t->fitPlane();
188  if (p) {
189  p->setVisible(true);
191  p->showNormals(true);
192 
193  // figure out where to store the fit plane
194  bool child =
195  ccCompass::mapMode; // in map mode, plane is stored as
196  // a child; in compass mode, plane
197  // is stored as parent
198 
199  if (m_childPlaneDeleted) // existing planes over-rule
200  {
201  child = true;
202  } else if (m_parentPlaneDeleted) {
203  child = false;
204  }
205 
206  if (child) // compass mode - trace becomes a child of the plane
207  {
208  t->addChild(p);
209  } else {
210  // yes - add trace as child of plane
211  ccHObject* parent = t->getParent();
212  parent->detachChild(t); // remove trace from it's parent
213  p->addChild(t); // add trace as child of plane
214  t->setVisible(false); // hide trace
215  parent->addChild(p); // add plane as child
216  m_app->addToDB(p);
217  }
218 
219  // report orientation to console for convenience
221  QString("[ccCompass] Trace orientation estimate = " +
222  p->getName()),
224  } else {
226  QString("[ccCompass] Not enough topography to fit "
227  "plane to trace."),
229  }
230  }
231 
232  m_trace_id = -1; // forget previous trace
233  m_childPlaneDeleted = false;
234  m_parentPlaneDeleted = false;
235  m_changed = false;
236  m_app->setSelectedInDB(t, false); // deselect this trace
238  true); // select it's parent instead
239 
241  }
242 }
243 
245  // is selected object a ccTrace?
246  ccTrace* t = dynamic_cast<ccTrace*>(obj); // try casting to ccTrace
247  if (t) // the object is a ccTrace!
248  {
249  // finish the previous trace
251 
252  // activate selected trace
253  t->setVisible(true);
254  m_preExisting = true;
255 
256  // check if trace has previously fitted planes before optimizing (and
257  // delete them if so)
258  if (ccFitPlane::isFitPlane(t->getParent())) {
259  ccHObject* parent = t->getParent();
260  parent->detachChild(t); // remove trace from the plane
261  parent->getParent()->addChild(t); // add trace to the plane's
262  // parent
263  m_app->removeFromDB(parent); // delete plane
264  m_app->addToDB(t); // add trace to db
265  m_parentPlaneDeleted = true;
266  } else {
267  for (unsigned idx = 0; idx < t->getChildrenNumber(); idx++) {
268  ccHObject* child = t->getChild(idx);
269  if (ccFitPlane::isFitPlane(child)) {
271  child); // n.b. removeFromDB also deletes the child
272  m_childPlaneDeleted = true;
273  }
274  }
275  }
276 
277  t->setActive(true);
278  m_trace_id = t->getUniqueID();
279  return true;
280  }
281  return false;
282 }
283 
284 // if this returns true, the undo button is enabled in the gui
286  return true; // yes - we can undo!
287 }
288 
289 // called when the undo button is clicked
291  ccTrace* t =
292  dynamic_cast<ccTrace*>(m_app->dbRootObject()->find(m_trace_id));
293  if (t) {
294  t->undoLast();
295  t->optimizePath();
297  }
298 }
static bool fitPlanes
Definition: ccCompass.h:263
static bool mapMode
Definition: ccCompass.h:267
virtual void setVisible(bool state)
Sets entity visibility.
static bool isFitPlane(ccHObject *object)
Definition: ccFitPlane.cpp:99
void showNormals(bool state) override
Sets normals visibility.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
ccHObject * find(unsigned uniqueID)
Finds an entity in this object hierarchy.
@ SELECTION_IGNORED
Definition: ecvHObject.h:608
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.
virtual void setSelectionBehavior(SelectionBehavior mode)
Sets selection behavior (when displayed)
Definition: ecvHObject.h:616
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
void setActive(bool isActive)
Definition: ccMeasurement.h:48
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
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
Plane (primitive)
Definition: ecvPlane.h:18
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
Definition: ccTool.h:18
ecvMainAppInterface * m_app
Definition: ccTool.h:67
bool m_precompute_gradient
Definition: ccTraceTool.h:69
void onNewSelection(const ccHObject::Container &selectedEntities) override
bool m_precompute_curvature
Definition: ccTraceTool.h:71
bool m_parentPlaneDeleted
Definition: ccTraceTool.h:66
void undo() override
void accept() override
bool m_changed
Definition: ccTraceTool.h:63
void toolDisactivated() override
Definition: ccTraceTool.cpp:26
bool pickupTrace(ccHObject *obj)
bool m_childPlaneDeleted
Definition: ccTraceTool.h:67
bool canUndo() override
virtual ~ccTraceTool()
Definition: ccTraceTool.cpp:18
void finishCurrentTrace()
bool m_preExisting
Definition: ccTraceTool.h:61
int m_trace_id
Definition: ccTraceTool.h:58
void toolActivated() override
Definition: ccTraceTool.cpp:21
void pointPicked(ccHObject *insertPoint, unsigned itemIdx, ccPointCloud *cloud, const CCVector3 &P) override
Definition: ccTraceTool.cpp:31
void cancel() override
static int COST_MODE
Definition: ccTrace.h:203
int insertWaypoint(int pointId)
Definition: ccTrace.cpp:92
ccFitPlane * fitPlane(int surface_effect_tolerance=10, float min_planarity=0.75f)
Definition: ccTrace.cpp:817
size_t waypoint_count() const
Definition: ccTrace.h:130
@ CURVE
Definition: ccTrace.h:196
@ GRADIENT
Definition: ccTrace.h:197
void buildCurvatureCost(QWidget *parent)
Definition: ccTrace.cpp:754
bool isGradientPrecomputed()
Definition: ccTrace.cpp:806
void undoLast()
Definition: ccTrace.h:121
void finalizePath()
Definition: ccTrace.cpp:237
void buildGradientCost(QWidget *parent)
Definition: ccTrace.cpp:680
bool isCurvaturePrecomputed()
Definition: ccTrace.cpp:811
bool optimizePath(int maxIterations=1000000)
Definition: ccTrace.cpp:149
unsigned size() const override
Returns the number of points.
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
virtual ccHObject * dbRootObject()=0
Returns DB root (as a ccHObject)
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual const ccHObject::Container & getSelectedEntities() const =0
Returns currently selected entities ("read only")
virtual void setSelectedInDB(ccHObject *obj, bool selected)=0
Selects or unselects an entity (in db tree)
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
virtual void removeFromDB(ccHObject *obj, bool autoDelete=true)=0
Removes an entity from main db tree.