ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ccTrace.h
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 #pragma once
9 
10 // #define DEBUG_PATH //n.b. uncomment this to write point-costs to a scalar
11 // field for debug purposes
12 
13 // LOCAL
14 #include "ccFitPlane.h"
15 #include "ccMeasurement.h"
16 
17 // CV_CORE_LIB
18 #include <DgmOctree.h>
21 #include <Jacobi.h>
22 #include <Neighbourhood.h>
23 #include <ScalarFieldTools.h>
24 #include <ecvProgressDialog.h>
25 
26 // CV_DB_LIB
27 #include <ecvColorTypes.h>
28 #include <ecvHObject.h>
29 #include <ecvPlane.h>
30 #include <ecvPointCloud.h>
31 #include <ecvPolyline.h>
32 #include <ecvScalarField.h>
33 #include <ecvSphere.h>
34 
35 // SYSTEM
36 #include <qmessagebox.h>
37 
38 #include <algorithm>
39 #include <deque>
40 #include <unordered_map>
41 #include <vector>
42 
43 /*
44 A ccTrace object is essentially a ccPolyline that is controlled/created by
45 "waypoints" and a least-cost path algorithm designe to pick fracture traces and
46 lithological contacts (i.e. follow edges or linear features in RGB space).
47 
48 If treated as a ccPolyline, it will behave as a conventional polyline going
49 through a series of points (including intermediate points between the
50 waypoints).
51 
52 If treated as a ccTrace object, then the waypoints can be manipulated and the
53 underlying ccPolyline recalculated. For convenience, the waypoints are also
54 drawn as bubbles.
55 
56 */
57 class ccTrace : public ccPolyline, public ccMeasurement {
58 public:
59  ccTrace(ccPointCloud* associatedCloud);
60  ccTrace(ccPolyline* obj); // used for constructing from polylines with the
61  // correct metadata
62  virtual ~ccTrace() {}
63 
64  // inherited from ccHObject
65  inline virtual CV_CLASS_ENUM getClassID() const override {
66  return CV_TYPES::POLY_LINE;
67  }
68 
69  /*
70  Adds waypoint to the end of this trace.
71  @Args
72  *pointIndex* = the index of the point (in the cloud associated with this
73  object) that is the new waypoint
74  */
75  void pushWaypoint(int pointId) { m_waypoints.push_back(pointId); }
76 
77  /*
78  Deletes the specified waypoint.
79  @Args
80  *pointIndex* = the index of the point (in the cloud associated with this
81  object) that represents the waypoint to be deleted. If this point is not a
82  waypoint then the function does nothing.
83  */
84  void deleteWaypoint(int pointId) {
85  m_waypoints.erase(
86  std::remove(m_waypoints.begin(), m_waypoints.end(), pointId),
87  m_waypoints.end());
88  }
89 
90  /*
91  Retrieves the global point index of the n'th waypoint
92  */
93  int getWaypoint(int n) { return m_waypoints[n]; }
94 
95  /*
96  Tries to insert/append the given waypoint based on its location. If the
97  point is within a "circle" including the start and end of the closest
98  segment then it "inserts" the point. Otherwise the point is appended onto
99  the end of the trace.
100 
101  Returns True if the point new waypoint was successfully added/inserted and
102  the path successfully updated.
103  */
104  int insertWaypoint(int pointId);
105 
106  /*
107  Returns the normal of the point at the specified index in this trace. If no
108  normal exists, it returns the vector 0,0,0.
109  */
110  CCVector3f getPointNormal(int pointIdx) {
111  if (!m_cloud->hasNormals()) {
112  return CCVector3f(0, 0, 0);
113  }
114 
115  return m_cloud->getPointNormal(getPointGlobalIndex(pointIdx));
116  }
117 
118  /*
119  Undo last action
120  */
121  void undoLast() {
122  if (!m_previous.empty()) {
123  m_waypoints.erase(m_waypoints.begin() +
124  m_previous.at(m_previous.size() - 1));
125  m_trace.clear(); // need to recalculate whole trace
126  m_previous.pop_back(); // remove waypoint
127  }
128  }
129 
130  size_t waypoint_count() const { return m_waypoints.size(); }
131 
132  /*
133  Calculates the most "structure-like" path between each waypoint using the A*
134  least cost path algorithm. Can be expensive...
135 
136  @Args
137  *maxIterations* = the maximum number of search iterations that are run
138  before the algorithm gives up. Default is lots.
139 
140  @Returns
141  -true if an optimal path was successfully identified
142  -false if the a path could not be found (iterations > maxIterations)
143  */
144  bool optimizePath(int maxIterations = 1000000);
145 
146  /*
147  Applies the optimized path to the underlying polyline object (allows saving
148  etc.).
149  */
150  void finalizePath();
151 
152  /*
153  Recalculates the path from scratch
154  */
155  void recalculatePath();
156 
157  /*
158  Fit a plane to this trace. Returns true if a plane was fit successfully.
159 
160  @Args
161  *surface_effect_tolerance* = the minimum allowable angle (in degrees)
162  between the average surface normals of the points [if they exist] and the
163  plane. This gets rid of fit planes that simply represent the outcrop
164  surface. *min_planarity* = the minimum allowable planarity (where a perfect
165  plane = 1, while a line or random point cloud = 0)
166 
167  @Return
168  The plane that was fitted, or a null pointer (0) if the plane is not
169  considered valid (by the above criterion)
170  */
171  ccFitPlane* fitPlane(int surface_effect_tolerance = 10,
172  float min_planarity = 0.75f);
173 
174  /*
175  Assigns the ID of this object to active scalar field (for each trace point).
176  */
177  void bakePathToScalarField();
178 
179  /*
180  Get the edge cost of going from p1 to p2 (this containts the "cost function"
181  to define what is "fracture like" and what is not)
182  */
183  int getSegmentCost(int p1, int p2);
184 
185  // functions for calculating cost SFs
186  void buildGradientCost(QWidget* parent);
187  void buildCurvatureCost(QWidget* parent);
188  bool isGradientPrecomputed();
189  bool isCurvaturePrecomputed();
190 
191  enum MODE {
192  RGB = 1, // default: cost function minimises local colour gradient &
193  // difference to start/end points
194  LIGHT = 2, // light points are low cost
195  DARK = 4, // dark points are low cost
196  CURVE = 8, // points with high curvature are low cost
197  GRADIENT = 16, // points with high gradient are low cost
198  DISTANCE = 32, // cost is euclidean distance
199  SCALAR = 64,
200  INV_SCALAR = 128
201  };
202 
203  static int COST_MODE;
204 
205 protected:
206  // overidden from ccHObject
207  virtual void drawMeOnly(CC_DRAW_CONTEXT& context) override;
208 
209  /*
210  Gets the closest waypoint to the point described by pID.
211  */
212  int getClosestWaypoint(int pointID);
213 
214  // contains grunt of shortest path algorithm. "offset" inserts points at the
215  // specified distance from the END of the trace (used for updating)
216  std::deque<int> optimizeSegment(int start, int end, int offset = 0);
217 
218  // specific cost algorithms (getSegmentCost(...) sums combinations of these
219  // depending on the COST_MODE flag. NOTE: to ensure each cost function makes
220  // an equal contribution to the result (when multiples are being used), each
221  // returns a value between 0 and 765 (the maximum r + g + bvalue),
222  // with the exception of getSegmentCostDist(...) which just returns a
223  // constant value (255), meaning it will find the least number of
224  // points between start and end (equal to the euclidean shortest path
225  // assuming point density is more or less constant).
226  int getSegmentCostRGB(int p1, int p2);
227  int getSegmentCostDark(int p1, int p2);
228  int getSegmentCostLight(int p1, int p2);
229  int getSegmentCostCurve(int p1, int p2);
230  int getSegmentCostGrad(int p1, int p2, float search_r);
231  int getSegmentCostDist(int p1, int p2);
232  int getSegmentCostScalar(int p1, int p2);
233  int getSegmentCostScalarInv(int p1, int p2);
234 
235  // calculate the search radius that should be used for the shortest path
236  // calcs
238 
239  // ccTrace variables
240  float m_relMarkerScale = 1.0f;
241  ccPointCloud* m_cloud = 0; // pointer to ccPointCloud object this is linked
242  // to (slightly different to polylines as we
243  // know this data is sampled from a real cloud)
244 
245  std::vector<std::deque<int>>
246  m_trace; // contains an ordered list of indices which define this
247  // trace. Note that indices representing nodes MAY be
248  // inserted twice.
249  std::vector<int> m_waypoints; // list of waypoint indices
250  std::vector<int> m_previous; // for undoing waypoints
251 private:
252  // class for storing point index & path costs (from the path start) in
253  // sorted lists
254  class Node {
255  public:
256  void set(int node_index, int node_total_cost, Node* prev_node) {
257  index = node_index;
258  total_cost = node_total_cost;
259  previous = prev_node;
260  }
261 
262  int index = -1;
263  int total_cost = 0;
264  Node* previous = nullptr;
265  };
266 
267  // class for comparing Node pointers in priority_queue
268  class Compare {
269  public:
270  bool operator()(Node* t1, Node* t2) {
271  // n.b. the priority queue puts "higher" priorities at the front of
272  // the queue. in this case, lower total_cost = "higher priority"
273  // hence we compare total_cost with the > operator
274  // i.e. t1 is less important than t2 if this.total_cost >
275  // t1.total_cost
276  return t1->total_cost > t2->total_cost; // compare based on cost
277  }
278  };
279 
280  // random vars that we keep to optimise speed
281  int m_start_rgb[3];
282  int m_end_rgb[3]; //[r,g,b] values for start and end nodes
285  float m_search_r;
286  float m_maxIterations;
287 
288  /*
289  Test if a point falls within a circle who's diameter equals the line from
290  segStart to segEnd. This is used to test if a newly added point should be
291  (1) appended to the end of the trace [falls outside of all segment-circles],
292  or (2) inserted to split a segment [falls into a segment-circle]
293  */
294  bool inCircle(const CCVector3* segStart,
295  const CCVector3* segEnd,
296  const CCVector3* query);
297 
298  // used by various constructors to do initialization
299  void init(ccPointCloud* associatedCloud);
300 
301  // used to update metadata flags
302  void updateMetadata();
303 
304  // static functions
305 public:
306  static bool isTrace(
307  ccHObject* object); // return true if object is a valid trace
308  // [regardless of it's class type]
309 };
Vector3Tpl< float > CCVector3f
Float 3D Vector.
Definition: CVGeom.h:801
int64_t CV_CLASS_ENUM
Type of object type flags (64 bits)
Definition: CVTypes.h:97
int offset
Generic handle to any of the 8 types of E57 element objects.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasNormals() const override
Returns whether normals are enabled or not.
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
Colored polyline.
Definition: ecvPolyline.h:24
int getSegmentCostScalar(int p1, int p2)
Definition: ccTrace.cpp:661
std::vector< int > m_previous
Definition: ccTrace.h:250
virtual CV_CLASS_ENUM getClassID() const override
Returns class ID.
Definition: ccTrace.h:65
virtual ~ccTrace()
Definition: ccTrace.h:62
int getSegmentCostDist(int p1, int p2)
Definition: ccTrace.cpp:659
std::deque< int > optimizeSegment(int start, int end, int offset=0)
Definition: ccTrace.cpp:258
static int COST_MODE
Definition: ccTrace.h:203
void pushWaypoint(int pointId)
Definition: ccTrace.h:75
std::vector< int > m_waypoints
Definition: ccTrace.h:249
float m_relMarkerScale
Definition: ccTrace.h:240
int getSegmentCostScalarInv(int p1, int p2)
Definition: ccTrace.cpp:670
ccPointCloud * m_cloud
Definition: ccTrace.h:241
int insertWaypoint(int pointId)
Definition: ccTrace.cpp:92
ccFitPlane * fitPlane(int surface_effect_tolerance=10, float min_planarity=0.75f)
Definition: ccTrace.cpp:817
void recalculatePath()
Definition: ccTrace.cpp:252
size_t waypoint_count() const
Definition: ccTrace.h:130
void bakePathToScalarField()
Definition: ccTrace.cpp:883
int getSegmentCostRGB(int p1, int p2)
Definition: ccTrace.cpp:477
void deleteWaypoint(int pointId)
Definition: ccTrace.h:84
static bool isTrace(ccHObject *object)
Definition: ccTrace.cpp:1102
@ CURVE
Definition: ccTrace.h:196
@ DISTANCE
Definition: ccTrace.h:198
@ INV_SCALAR
Definition: ccTrace.h:200
@ GRADIENT
Definition: ccTrace.h:197
@ RGB
Definition: ccTrace.h:192
@ SCALAR
Definition: ccTrace.h:199
@ LIGHT
Definition: ccTrace.h:194
@ DARK
Definition: ccTrace.h:195
int getClosestWaypoint(int pointID)
void buildCurvatureCost(QWidget *parent)
Definition: ccTrace.cpp:754
bool isGradientPrecomputed()
Definition: ccTrace.cpp:806
CCVector3f getPointNormal(int pointIdx)
Definition: ccTrace.h:110
int getSegmentCostLight(int p1, int p2)
Definition: ccTrace.cpp:535
void undoLast()
Definition: ccTrace.h:121
void finalizePath()
Definition: ccTrace.cpp:237
int getSegmentCostGrad(int p1, int p2, float search_r)
Definition: ccTrace.cpp:590
int getSegmentCostDark(int p1, int p2)
Definition: ccTrace.cpp:524
int getSegmentCost(int p1, int p2)
Definition: ccTrace.cpp:450
virtual void drawMeOnly(CC_DRAW_CONTEXT &context) override
Draws the entity only (not its children)
Definition: ccTrace.cpp:939
std::vector< std::deque< int > > m_trace
Definition: ccTrace.h:246
void buildGradientCost(QWidget *parent)
Definition: ccTrace.cpp:680
ccTrace(ccPointCloud *associatedCloud)
Definition: ccTrace.cpp:15
float calculateOptimumSearchRadius()
Definition: ccTrace.cpp:896
bool isCurvaturePrecomputed()
Definition: ccTrace.cpp:811
bool optimizePath(int maxIterations=1000000)
Definition: ccTrace.cpp:149
int getWaypoint(int n)
Definition: ccTrace.h:93
int getSegmentCostCurve(int p1, int p2)
Definition: ccTrace.cpp:540
std::vector< PointDescriptor > NeighboursSet
A set of neighbours.
Definition: DgmOctree.h:133
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
ImGuiContext * context
Definition: Window.cpp:76
@ POLY_LINE
Definition: CVTypes.h:112
Display context.
Structure used during nearest neighbour search.
Definition: DgmOctree.h:101