Main MRPT website > C++ reference for MRPT 1.9.9
CAngularObservationMesh.h
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 #ifndef opengl_CAngularObservationMesh_H
10 #define opengl_CAngularObservationMesh_H
11 
15 #include <mrpt/math/CMatrixB.h>
17 #include <mrpt/maps/CPointsMap.h>
19 
20 #include <mrpt/math/geometry.h>
21 
22 namespace mrpt
23 {
24 namespace opengl
25 {
26 /**
27  * A mesh built from a set of 2D laser scan observations.
28  * Each element of this set is a single scan through the yaw, given a specific
29  * pitch.
30  * Each scan has a mrpt::poses::CPose3D identifying the origin of the scan,
31  * which ideally is the
32  * same for every one of them.
33  *
34  * <div align="center">
35  * <table border="0" cellspan="4" cellspacing="4" style="border-width: 1px;
36  * border-style: solid;">
37  * <tr> <td> mrpt::opengl::CAngularObservationMesh </td> <td> \image html
38  * preview_CAngularObservationMesh.png </td> </tr>
39  * </table>
40  * </div>
41  *
42  * \ingroup mrpt_maps_grp
43  */
45 {
47  public:
48  /**
49  * Range specification type, with several uses.
50  */
51  struct TDoubleRange
52  {
53  private:
54  /**
55  * Range type.
56  * If 0, it's specified by an initial and a final value, and an
57  * increment.
58  * If 1, it's specified by an initial and a final value, and a fixed
59  * size of samples.
60  * If 2, it's specified by an aperture, a fixed size of samples and a
61  * boolean variable controlling direction. This type is always
62  * zero-centered.
63  */
64  char rangeType;
65  /**
66  * Union type with the actual data.
67  * \sa rangeType
68  */
69  union rd {
70  struct
71  {
72  double initial;
73  double final;
74  double increment;
75  } mode0;
76  struct
77  {
78  double initial;
79  double final;
80  size_t amount;
81  } mode1;
82  struct
83  {
84  double aperture;
85  size_t amount;
86  bool negToPos;
87  } mode2;
89 
90  public:
91  /**
92  * Constructor from initial value, final value and range.
93  */
94  TDoubleRange(double a, double b, double c) : rangeType(0)
95  {
99  }
100  /**
101  * Constructor from initial value, final value and amount of samples.
102  */
103  TDoubleRange(double a, double b, size_t c) : rangeType(1)
104  {
108  }
109  /**
110  * Constructor from aperture, amount of samples and scan direction.
111  */
112  TDoubleRange(double a, size_t b, bool c) : rangeType(2)
113  {
117  }
118  /**
119  * Creates a range of values from the initial value, the final value
120  * and the increment.
121  * \throw std::logic_error if the increment is zero.
122  */
124  double initial, double final, double increment)
125  {
126  if (increment == 0)
127  throw std::logic_error("Invalid increment value.");
128  return TDoubleRange(initial, final, increment);
129  }
130  /**
131  * Creates a range of values from the initial value, the final value
132  * and a desired amount of samples.
133  */
135  double initial, double final, size_t amount)
136  {
137  return TDoubleRange(initial, final, amount);
138  }
139  /**
140  * Creates a zero-centered range of values from an aperture, an amount
141  * of samples and a direction.
142  */
144  double aperture, size_t amount, bool negToPos = true)
145  {
147  }
148  /**
149  * Returns the total aperture of the range.
150  * \throw std::logic_error on invalid range type.
151  */
152  inline double aperture() const
153  {
154  switch (rangeType)
155  {
156  case 0:
161  ? fabs(
164  : 0;
165  case 1:
167  case 2:
168  return rangeData.mode2.aperture;
169  default:
170  throw std::logic_error("Unknown range type.");
171  }
172  }
173  /**
174  * Returns the first value of the range.
175  * \throw std::logic_error on invalid range type.
176  */
177  inline double initialValue() const
178  {
179  switch (rangeType)
180  {
181  case 0:
182  case 1:
183  return rangeData.mode0.initial;
184  case 2:
185  return rangeData.mode2.negToPos
186  ? -rangeData.mode2.aperture / 2
187  : rangeData.mode2.aperture / 2;
188  default:
189  throw std::logic_error("Unknown range type.");
190  }
191  }
192  /**
193  * Returns the last value of the range.
194  * \throw std::logic_error on invalid range type.
195  */
196  inline double finalValue() const
197  {
198  switch (rangeType)
199  {
200  case 0:
207  case 1:
208  return rangeData.mode1.final;
209  case 2:
210  return rangeData.mode2.negToPos
211  ? rangeData.mode2.aperture / 2
212  : -rangeData.mode2.aperture / 2;
213  default:
214  throw std::logic_error("Unknown range type.");
215  }
216  }
217  /**
218  * Returns the increment between two consecutive values of the range.
219  * \throw std::logic_error on invalid range type.
220  */
221  inline double increment() const
222  {
223  switch (rangeType)
224  {
225  case 0:
226  return rangeData.mode0.increment;
227  case 1:
229  static_cast<double>(rangeData.mode1.amount - 1);
230  case 2:
231  return rangeData.mode2.negToPos
233  static_cast<double>(
234  rangeData.mode2.amount - 1)
236  static_cast<double>(
237  rangeData.mode2.amount - 1);
238  default:
239  throw std::logic_error("Unknown range type.");
240  }
241  }
242  /**
243  * Returns the total amount of values in this range.
244  * \throw std::logic_error on invalid range type.
245  */
246  inline size_t amount() const
247  {
248  switch (rangeType)
249  {
250  case 0:
255  ? 1 + static_cast<size_t>(
256  ceil(
260  : 1;
261  case 1:
262  return rangeData.mode1.amount;
263  case 2:
264  return rangeData.mode2.amount;
265  default:
266  throw std::logic_error("Unknown range type.");
267  }
268  }
269  /**
270  * Gets a vector with every value in the range.
271  * \throw std::logic_error on invalid range type.
272  */
273  void values(std::vector<double>& vals) const;
274  /**
275  * Returns the direction of the scan. True if the increment is
276  * positive, false otherwise.
277  * \throw std::logic_error on invalid range type.
278  */
279  inline bool negToPos() const
280  {
281  switch (rangeType)
282  {
283  case 0:
285  case 1:
286  return mrpt::utils::sign(
288  rangeData.mode1.initial) > 0;
289  case 2:
290  return rangeData.mode2.negToPos;
291  default:
292  throw std::logic_error("Unknown range type.");
293  }
294  }
295  };
296 
297  void getBoundingBox(
298  mrpt::math::TPoint3D& bb_min,
299  mrpt::math::TPoint3D& bb_max) const override;
300 
301  protected:
302  /** Updates the mesh, if needed. It's a const method, but modifies mutable
303  * content. */
304  void updateMesh() const;
305  /** Actual set of triangles to be displayed. */
306  mutable std::vector<CSetOfTriangles::TTriangle> triangles;
307  /** Internal method to add a triangle to the mutable mesh. */
309  const mrpt::math::TPoint3D& p1, const mrpt::math::TPoint3D& p2,
310  const mrpt::math::TPoint3D& p3) const;
311  /** Whether the mesh will be displayed wireframe or solid. */
313  /** Mutable variable which controls if the object has suffered any change
314  * since last time the mesh was updated. */
315  mutable bool meshUpToDate;
316  /** Whether the object may present transparencies or not. */
318  /** Mutable object with the mesh's points. */
320  /** Scan validity matrix. */
322  /** Observation pitch range. When containing exactly two elements, they
323  * represent the bounds. */
324  std::vector<double> pitchBounds;
325  /** Actual scan set which is used to generate the mesh. */
326  std::vector<mrpt::obs::CObservation2DRangeScan> scanSet;
327 
328  public:
329  /**
330  * Basic constructor.
331  */
333  : mWireframe(true),
334  meshUpToDate(false),
335  mEnableTransparency(true),
336  actualMesh(0, 0),
337  validityMatrix(0, 0),
338  pitchBounds(),
339  scanSet()
340  {
341  }
342  /** Empty destructor. */
344  /**
345  * Returns whether the object is configured as wireframe or solid.
346  */
347  inline bool isWireframe() const { return mWireframe; }
348  /**
349  * Sets the display mode for the object. True=wireframe, False=solid.
350  */
351  inline void setWireframe(bool enabled = true)
352  {
353  mWireframe = enabled;
355  }
356  /**
357  * Returns whether the object may be transparent or not.
358  */
359  inline bool isTransparencyEnabled() const { return mEnableTransparency; }
360  /**
361  * Enables or disables transparencies.
362  */
363  inline void enableTransparency(bool enabled = true)
364  {
365  mEnableTransparency = enabled;
367  }
368  /**
369  * Renderizes the object.
370  * \sa mrpt::opengl::CRenderizable
371  */
372  virtual void render_dl() const override;
373  /**
374  * Traces a ray to the object, returning the distance to a given pose
375  * through its X axis.
376  * \sa mrpt::opengl::CRenderizable,trace2DSetOfRays,trace1DSetOfRays
377  */
378  bool traceRay(const mrpt::poses::CPose3D& o, double& dist) const override;
379  /**
380  * Sets the pitch bounds for this range.
381  */
382  void setPitchBounds(const double initial, const double final);
383  /**
384  * Sets the pitch bounds for this range.
385  */
386  void setPitchBounds(const std::vector<double>& bounds);
387  /**
388  * Gets the initial and final pitch bounds for this range.
389  */
390  void getPitchBounds(double& initial, double& final) const;
391  /**
392  * Gets the pitch bounds for this range.
393  */
394  void getPitchBounds(std::vector<double>& bounds) const;
395  /**
396  * Gets the scan set.
397  */
398  void getScanSet(
399  std::vector<mrpt::obs::CObservation2DRangeScan>& scans) const;
400  /**
401  * Sets the scan set.
402  */
404  const std::vector<mrpt::obs::CObservation2DRangeScan>& scans);
405  /**
406  * Gets the mesh as a set of triangles, for displaying them.
407  * \sa generateSetOfTriangles(std::vector<TPolygon3D>
408  * &),mrpt::opengl::CSetOfTriangles,mrpt::opengl::CSetOfTriangles::TTriangle
409  */
411  /**
412  * Returns the scanned points as a 3D point cloud. The target pointmap must
413  * be passed as a pointer to allow the use of any derived class.
414  */
415  void generatePointCloud(mrpt::maps::CPointsMap* out_map) const;
416  /**
417  * Gets a set of lines containing the traced rays, for displaying them.
418  * \sa getUntracedRays,mrpt::opengl::CSetOfLines
419  */
420  void getTracedRays(CSetOfLines::Ptr& res) const;
421  /**
422  * Gets a set of lines containing the untraced rays, up to a specified
423  * distance, for displaying them.
424  * \sa getTracedRays,mrpt::opengl::CSetOfLines
425  */
426  void getUntracedRays(CSetOfLines::Ptr& res, double dist) const;
427  /**
428  * Gets the mesh as a set of polygons, to work with them.
429  * \sa generateSetOfTriangles(mrpt::opengl::CSetOfTriangles &)
430  */
431  void generateSetOfTriangles(std::vector<mrpt::math::TPolygon3D>& res) const;
432  /**
433  * Retrieves the full mesh, along with the validity matrix.
434  */
437  mrpt::math::CMatrixBool& validity) const
438  {
439  if (!meshUpToDate) updateMesh();
440  pts = actualMesh;
441  validity = validityMatrix;
442  }
443 
444  private:
445  /**
446  * Internal functor class to trace a ray.
447  */
448  template <class T>
449  class FTrace1D
450  {
451  protected:
453  const T& e;
454  std::vector<double>& values;
455  std::vector<char>& valid;
456 
457  public:
459  const T& s, const mrpt::poses::CPose3D& p, std::vector<double>& v,
460  std::vector<char>& v2)
461  : initial(p), e(s), values(v), valid(v2)
462  {
463  }
464  void operator()(double yaw)
465  {
466  double dist;
467  const mrpt::poses::CPose3D pNew =
468  initial + mrpt::poses::CPose3D(0.0, 0.0, 0.0, yaw, 0.0, 0.0);
469  if (e->traceRay(pNew, dist))
470  {
471  values.push_back(dist);
472  valid.push_back(1);
473  }
474  else
475  {
476  values.push_back(0);
477  valid.push_back(0);
478  }
479  }
480  };
481  /**
482  * Internal functor class to trace a set of rays.
483  */
484  template <class T>
485  class FTrace2D
486  {
487  protected:
488  const T& e;
492  std::vector<mrpt::obs::CObservation2DRangeScan>& vObs;
494 
495  public:
497  const T& s, const mrpt::poses::CPose3D& p,
500  std::vector<mrpt::obs::CObservation2DRangeScan>& obs,
501  const mrpt::poses::CPose3D& b)
502  : e(s), initial(p), caom(om), yaws(y), vObs(obs), pBase(b)
503  {
504  }
505  void operator()(double pitch)
506  {
507  std::vector<double> yValues;
508  yaws.values(yValues);
511  const mrpt::poses::CPose3D pNew =
512  initial + mrpt::poses::CPose3D(0, 0, 0, 0, pitch, 0);
513  std::vector<double> values;
514  std::vector<char> valid;
515  size_t nY = yValues.size();
516  values.reserve(nY);
517  valid.reserve(nY);
518  for_each(
519  yValues.begin(), yValues.end(),
520  FTrace1D<T>(e, pNew, values, valid));
521  o.aperture = yaws.aperture();
522  o.rightToLeft = yaws.negToPos();
523  o.maxRange = 10000;
524  o.sensorPose = pNew;
525  o.deltaPitch = 0;
526  o.resizeScan(values.size());
527  for (size_t i = 0; i < values.size(); i++)
528  {
529  o.setScanRange(i, values[i]);
530  o.setScanRangeValidity(i, valid[i] != 0);
531  }
532  vObs.push_back(o);
533  }
534  };
535 
536  public:
537  /**
538  * 2D ray tracing (will generate a 3D mesh). Given an object and two
539  * ranges, realizes a scan from the initial pose and stores it in a
540  * CAngularObservationMesh object.
541  * The objective may be a COpenGLScene, a CRenderizable or any children of
542  * its.
543  * \sa mrpt::opengl::CRenderizable,mrpt::opengl::COpenGLScene.
544  */
545  template <class T>
546  static void trace2DSetOfRays(
547  const T& e, const mrpt::poses::CPose3D& initial,
548  CAngularObservationMesh::Ptr& caom, const TDoubleRange& pitchs,
549  const TDoubleRange& yaws);
550  /**
551  * 2D ray tracing (will generate a vectorial mesh inside a plane). Given an
552  * object and a range, realizes a scan from the initial pose and stores it
553  * in a CObservation2DRangeScan object.
554  * The objective may be a COpenGLScene, a CRenderizable or any children of
555  * its.
556  * \sa mrpt::opengl::CRenderizable,mrpt::opengl::COpenGLScene.
557  */
558  template <class T>
559  static void trace1DSetOfRays(
560  const T& e, const mrpt::poses::CPose3D& initial,
562  {
563  std::vector<double> yValues;
564  yaws.values(yValues);
565  std::vector<float> scanValues;
566  std::vector<char> valid;
567  size_t nV = yaws.amount();
568  scanValues.reserve(nV);
569  valid.reserve(nV);
570  for_each(
571  yValues.begin(), yValues.end(),
572  FTrace1D<T>(e, initial, scanValues, valid));
573  obs.aperture = yaws.aperture();
574  obs.rightToLeft = yaws.negToPos();
575  obs.maxRange = 10000;
576  obs.sensorPose = initial;
577  obs.deltaPitch = 0;
578  obs.scan = scanValues;
579  obs.validRange = valid;
580  }
581 };
582 
583 template <class T>
585  const T& e, const mrpt::poses::CPose3D& initial,
586  CAngularObservationMesh::Ptr& caom, const TDoubleRange& pitchs,
587  const TDoubleRange& yaws)
588 {
589  std::vector<double> pValues;
590  pitchs.values(pValues);
591  std::vector<mrpt::obs::CObservation2DRangeScan> vObs;
592  vObs.reserve(pValues.size());
593  for_each(
594  pValues.begin(), pValues.end(),
595  FTrace2D<T>(e, initial, caom, yaws, vObs, initial));
596  caom->mWireframe = false;
597  caom->mEnableTransparency = false;
598  caom->setPitchBounds(pValues);
599  caom->setScanSet(vObs);
600 }
601 }
602 }
603 #endif
#define DEFINE_SERIALIZABLE(class_name)
This declaration must be inserted in all CSerializable classes definition, within the class declarati...
A cloud of points in 2D or 3D, which can be built from a sequence of laser scans or other sensors.
Definition: CPointsMap.h:67
This class is a "CSerializable" wrapper for "CMatrixBool".
Definition: CMatrixB.h:25
Declares a matrix of booleans (non serializable).
A "CObservation"-derived class that represents a 2D range scan measurement (typically from a laser sc...
float maxRange
The maximum range allowed by the device, in meters (e.g.
mrpt::utils::ContainerReadOnlyProxyAccessor< std::vector< char > > validRange
It's false (=0) on no reflected rays, referenced to elements in scan.
double deltaPitch
If the laser gathers data by sweeping in the pitch/elevation angle, this holds the increment in "pitc...
float aperture
The "aperture" or field-of-view of the range finder, in radians (typically M_PI = 180 degrees).
mrpt::poses::CPose3D sensorPose
The 6D pose of the sensor on the robot at the moment of starting the scan.
bool rightToLeft
The scanning direction: true=counterclockwise; false=clockwise.
mrpt::utils::ContainerReadOnlyProxyAccessor< std::vector< float > > scan
The range values of the scan, in meters.
void resizeScan(const size_t len)
Resizes all data vectors to allocate a given number of scan rays.
void setScanRangeValidity(const size_t i, const bool val)
void setScanRange(const size_t i, const float val)
FTrace1D(const T &s, const mrpt::poses::CPose3D &p, std::vector< double > &v, std::vector< char > &v2)
Internal functor class to trace a set of rays.
FTrace2D(const T &s, const mrpt::poses::CPose3D &p, CAngularObservationMesh::Ptr &om, const CAngularObservationMesh::TDoubleRange &y, std::vector< mrpt::obs::CObservation2DRangeScan > &obs, const mrpt::poses::CPose3D &b)
const CAngularObservationMesh::TDoubleRange & yaws
std::vector< mrpt::obs::CObservation2DRangeScan > & vObs
A mesh built from a set of 2D laser scan observations.
void addTriangle(const mrpt::math::TPoint3D &p1, const mrpt::math::TPoint3D &p2, const mrpt::math::TPoint3D &p3) const
Internal method to add a triangle to the mutable mesh.
mrpt::math::CMatrixB validityMatrix
Scan validity matrix.
void generateSetOfTriangles(CSetOfTriangles::Ptr &res) const
Gets the mesh as a set of triangles, for displaying them.
void setPitchBounds(const double initial, const double final)
Sets the pitch bounds for this range.
std::vector< CSetOfTriangles::TTriangle > triangles
Actual set of triangles to be displayed.
bool mWireframe
Whether the mesh will be displayed wireframe or solid.
mrpt::math::CMatrixTemplate< mrpt::math::TPoint3D > actualMesh
Mutable object with the mesh's points.
void generatePointCloud(mrpt::maps::CPointsMap *out_map) const
Returns the scanned points as a 3D point cloud.
void getPitchBounds(double &initial, double &final) const
Gets the initial and final pitch bounds for this range.
void setWireframe(bool enabled=true)
Sets the display mode for the object.
bool meshUpToDate
Mutable variable which controls if the object has suffered any change since last time the mesh was up...
bool traceRay(const mrpt::poses::CPose3D &o, double &dist) const override
Traces a ray to the object, returning the distance to a given pose through its X axis.
void getTracedRays(CSetOfLines::Ptr &res) const
Gets a set of lines containing the traced rays, for displaying them.
static void trace2DSetOfRays(const T &e, const mrpt::poses::CPose3D &initial, CAngularObservationMesh::Ptr &caom, const TDoubleRange &pitchs, const TDoubleRange &yaws)
2D ray tracing (will generate a 3D mesh).
virtual ~CAngularObservationMesh()
Empty destructor.
bool mEnableTransparency
Whether the object may present transparencies or not.
void updateMesh() const
Updates the mesh, if needed.
bool isWireframe() const
Returns whether the object is configured as wireframe or solid.
std::vector< double > pitchBounds
Observation pitch range.
void getUntracedRays(CSetOfLines::Ptr &res, double dist) const
Gets a set of lines containing the untraced rays, up to a specified distance, for displaying them.
void getActualMesh(mrpt::math::CMatrixTemplate< mrpt::math::TPoint3D > &pts, mrpt::math::CMatrixBool &validity) const
Retrieves the full mesh, along with the validity matrix.
std::vector< mrpt::obs::CObservation2DRangeScan > scanSet
Actual scan set which is used to generate the mesh.
std::shared_ptr< CAngularObservationMesh > Ptr
void getBoundingBox(mrpt::math::TPoint3D &bb_min, mrpt::math::TPoint3D &bb_max) const override
Evaluates the bounding box of this object (including possible children) in the coordinate frame of th...
virtual void render_dl() const override
Renderizes the object.
void getScanSet(std::vector< mrpt::obs::CObservation2DRangeScan > &scans) const
Gets the scan set.
void enableTransparency(bool enabled=true)
Enables or disables transparencies.
static void trace1DSetOfRays(const T &e, const mrpt::poses::CPose3D &initial, mrpt::obs::CObservation2DRangeScan &obs, const TDoubleRange &yaws)
2D ray tracing (will generate a vectorial mesh inside a plane).
bool setScanSet(const std::vector< mrpt::obs::CObservation2DRangeScan > &scans)
Sets the scan set.
bool isTransparencyEnabled() const
Returns whether the object may be transparent or not.
A renderizable object suitable for rendering with OpenGL's display lists.
EIGEN_STRONG_INLINE void notifyChange() const
Must be called to notify that the object has changed (so, the display list must be updated)
std::shared_ptr< CSetOfLines > Ptr
Definition: CSetOfLines.h:37
std::shared_ptr< CSetOfTriangles > Ptr
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
Definition: CPose3D.h:89
const GLdouble * v
Definition: glext.h:3678
GLuint res
Definition: glext.h:7268
const GLubyte * c
Definition: glext.h:6313
GLboolean GLenum GLenum GLvoid * values
Definition: glext.h:3582
GLenum GLint GLint y
Definition: glext.h:3538
GLubyte GLubyte b
Definition: glext.h:6279
GLfloat GLfloat GLfloat v2
Definition: glext.h:4107
GLfloat GLfloat p
Definition: glext.h:6305
GLubyte GLubyte GLubyte a
Definition: glext.h:6279
GLdouble s
Definition: glext.h:3676
int sign(T x)
Returns the sign of X as "1" or "-1".
Definition: bits.h:121
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
Lightweight 3D point.
Range specification type, with several uses.
TDoubleRange(double a, size_t b, bool c)
Constructor from aperture, amount of samples and scan direction.
double increment() const
Returns the increment between two consecutive values of the range.
double aperture() const
Returns the total aperture of the range.
static TDoubleRange CreateFromAperture(double aperture, size_t amount, bool negToPos=true)
Creates a zero-centered range of values from an aperture, an amount of samples and a direction.
double initialValue() const
Returns the first value of the range.
TDoubleRange(double a, double b, double c)
Constructor from initial value, final value and range.
size_t amount() const
Returns the total amount of values in this range.
bool negToPos() const
Returns the direction of the scan.
double finalValue() const
Returns the last value of the range.
static TDoubleRange CreateFromIncrement(double initial, double final, double increment)
Creates a range of values from the initial value, the final value and the increment.
void values(std::vector< double > &vals) const
Gets a vector with every value in the range.
union mrpt::opengl::CAngularObservationMesh::TDoubleRange::rd rangeData
static TDoubleRange CreateFromAmount(double initial, double final, size_t amount)
Creates a range of values from the initial value, the final value and a desired amount of samples.
TDoubleRange(double a, double b, size_t c)
Constructor from initial value, final value and amount of samples.
struct mrpt::opengl::CAngularObservationMesh::TDoubleRange::rd::@57 mode0
struct mrpt::opengl::CAngularObservationMesh::TDoubleRange::rd::@58 mode1
struct mrpt::opengl::CAngularObservationMesh::TDoubleRange::rd::@59 mode2



Page generated by Doxygen 1.9.1 for MRPT 1.9.9 Git: 63ea9d1f1 Thu Nov 23 00:06:53 2017 +0100 at mar 26 may 2026 12:19:29 CEST