Shape2p5.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2023 Jose Luis Blanco Claraco |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under 3-clause BSD License |
7  | See COPYING |
8  +-------------------------------------------------------------------------+ */
9 
10 #include <box2d/b2_settings.h> // b2_maxPolygonVertices
11 #include <mrpt/maps/CSimplePointsMap.h>
12 #include <mrpt/math/TBoundingBox.h>
13 #include <mrpt/math/TLine2D.h>
14 #include <mrpt/math/TObject2D.h>
15 #include <mrpt/math/geometry.h>
16 #include <mrpt/opengl/COpenGLScene.h>
17 #include <mrpt/opengl/CPointCloud.h>
18 #include <mrpt/opengl/CSetOfLines.h>
19 #include <mrpt/opengl/CSetOfTriangles.h>
20 #include <mrpt/opengl/CTexturedPlane.h>
21 #include <mrpt/opengl/stock_objects.h>
22 #include <mvsim/Shape2p5.h>
23 
24 #include <cmath>
25 #include <iostream>
26 #include <queue>
27 
28 // Uncomment only for development debugging
29 //#define DEBUG_DUMP_ALL_TEMPORARY_GRIDS
30 //#define DEBUG_DUMP_TRIANGLES
31 
32 using namespace mvsim;
33 
34 using mrpt::math::TPoint2Df;
35 using mrpt::math::TPoint3Df;
36 
37 constexpr uint8_t CELL_UNDEFINED = 0x80;
38 constexpr uint8_t CELL_OCCUPIED = 0x00;
39 constexpr uint8_t CELL_FREE = 0xff;
40 constexpr uint8_t CELL_VISITED = 0x40;
41 
42 double Shape2p5::volume() const
43 {
44  return std::abs(mrpt::math::signedArea(getContour())) *
45  std::abs(zMin_ - zMax_);
46 }
47 
49 {
50  using mrpt::math::TPoint3Df;
51 
52  const auto& ca = this->getContour();
53  const auto& cb = s.getContour();
54 
55  // gross BB:
56  auto bb = mrpt::math::TBoundingBoxf::PlusMinusInfinity();
57  for (const auto& p : ca) bb.updateWithPoint(TPoint3Df(p.x, p.y, zMin()));
58  for (const auto& p : cb) bb.updateWithPoint(TPoint3Df(p.x, p.y, s.zMin()));
59 
60  bb.updateWithPoint(TPoint3Df(bb.min.x, bb.min.y, s.zMax()));
61  bb.updateWithPoint(TPoint3Df(bb.max.x, bb.max.y, this->zMax()));
62 
63  // convert to triangles:
64  Shape2p5 newShape;
65  newShape.buildInit({bb.min.x, bb.min.y}, {bb.max.x, bb.max.y});
66  for (size_t i = 0; i < ca.size(); i++)
67  {
68  size_t im1 = i == 0 ? (ca.size() - 1) : i - 1;
69  const auto p0 = ca.at(im1);
70  const auto p1 = ca.at(i);
71 
72  mrpt::opengl::TTriangle t;
73  t.vertex(0) = TPoint3Df(p0.x, p0.y, bb.min.z);
74  t.vertex(1) = TPoint3Df(p1.x, p1.y, bb.min.z);
75  t.vertex(2) = TPoint3Df(p1.x, p1.y, bb.max.z);
76  newShape.buildAddTriangle(t);
77  }
78  for (size_t i = 0; i < cb.size(); i++)
79  {
80  size_t im1 = i == 0 ? (cb.size() - 1) : i - 1;
81  const auto p0 = cb.at(im1);
82  const auto p1 = cb.at(i);
83 
84  mrpt::opengl::TTriangle t;
85  t.vertex(0) = TPoint3Df(p0.x, p0.y, bb.min.z);
86  t.vertex(1) = TPoint3Df(p1.x, p1.y, bb.min.z);
87  t.vertex(2) = TPoint3Df(p1.x, p1.y, bb.max.z);
88  newShape.buildAddTriangle(t);
89  }
90 
91  // re-generate again:
92  *this = newShape;
93 }
94 
95 const mrpt::math::TPolygon2D& Shape2p5::getContour() const
96 {
97  if (!contour_) computeShape();
98  return *contour_;
99 }
100 
101 // For debugging only:
102 #ifdef DEBUG_DUMP_TRIANGLES
103 static auto glDebugTriangles = mrpt::opengl::CSetOfTriangles::Create();
104 #endif
105 
107  const mrpt::math::TPoint2Df& bbMin, const mrpt::math::TPoint2Df& bbMax,
108  int numCells)
109 {
110  contour_.reset(); // start from scratch
111 
112  // grid resolution:
113  const float r = (bbMax - bbMin).norm() / numCells;
114  // border to ensure we have a free row/col all around the shape
115  const float b = r * 1.5f;
116  grid_.emplace(bbMin.x - b, bbMax.x + b, bbMin.y - b, bbMax.y + b, r);
117 
118  zMin_ = std::numeric_limits<float>::max();
119  zMax_ = -std::numeric_limits<float>::max();
120  grid_->fill(CELL_UNDEFINED);
121 
122 #ifdef DEBUG_DUMP_TRIANGLES
123  glDebugTriangles->clearTriangles();
124 #endif
125 }
126 
127 void Shape2p5::buildAddPoint(const mrpt::math::TPoint3Df& pt)
128 {
129  mrpt::keep_max(zMax_, pt.z);
130  mrpt::keep_min(zMin_, pt.z);
131  uint8_t* c = grid_->cellByPos(pt.x, pt.y);
132  ASSERT_(c);
133  *c = CELL_OCCUPIED;
134 }
135 
136 void Shape2p5::buildAddTriangle(const mrpt::opengl::TTriangle& t)
137 {
138  const float step = grid_->getResolution();
139 
140  for (int i0 = 0; i0 < 3; i0++)
141  {
142  const int i1 = (i0 + 1) % 3;
143  const auto& v0 = t.vertex(i0);
144  const auto& v1 = t.vertex(i1);
145 
146  const auto v01 = v1 - v0;
147 
148  const int nSteps = static_cast<int>(std::ceil(v01.norm() / step));
149 
150  mrpt::math::TPoint3Df p = v0;
151  const mrpt::math::TVector3Df Ap = v01 * (1.0f / nSteps);
152 
153  for (int s = 0; s < nSteps; s++, p += Ap)
154  {
155  uint8_t* c = grid_->cellByPos(p.x, p.y);
156  if (!c) continue;
157 
158  *c = CELL_OCCUPIED;
159  mrpt::keep_max(zMax_, p.z);
160  mrpt::keep_min(zMin_, p.z);
161  }
162  }
163 
164 #ifdef DEBUG_DUMP_TRIANGLES
165  glDebugTriangles->insertTriangle(t);
166 #endif
167 }
168 
169 // Computes contour_ from the contents in grid_
171 {
172  ASSERT_(grid_);
173  ASSERT_(!contour_);
174 
175 #ifdef DEBUG_DUMP_ALL_TEMPORARY_GRIDS
176  // Debug save initial grid:
178 #endif
179 
180  // 0) Filter spurious occupied cells, due to (rare) lost points in the 3D
181  // model:
183 
184  // 1) Flood-fill the grid with "FREE color" to allow detecting the outer
185  // shape:
187 
188  // 2) Detect the outer contour with full grid resolution:
189  const mrpt::math::TPolygon2D rawGridContour = internalGridContour();
190 
191  // 3) convex hull:
192  // Eventually, b2Box library will use convex hull anyway, so let's use it
193  // here too:
194 
195  // 4) Polygon pruning until edge count is <= b2_maxPolygonVertices
196  const auto finalPoly = internalPrunePolygon(rawGridContour);
197 
198  // 5) Save result if output structure:
199  contour_.emplace(finalPoly);
200 
201 // DEBUG:
202 #ifdef DEBUG_DUMP_ALL_TEMPORARY_GRIDS
203  debugSaveGridTo3DSceneFile(rawGridContour);
204 #endif
205 
206  grid_.reset();
207 
208 #ifdef DEBUG_DUMP_TRIANGLES
209  {
210  static int cnt = 0;
211  mrpt::opengl::COpenGLScene scene;
212  scene.insert(glDebugTriangles);
213  scene.saveToFile(
214  mrpt::format("debug_shape2p5_triangles_%04i.3Dscene", cnt++));
215  }
216 #endif
217 }
218 
220  const mrpt::math::TPolygon2D& contour, const float zMin, const float zMax)
221 {
222  grid_.reset();
223  contour_ = contour;
224  zMin_ = zMin;
225  zMax_ = zMax;
226 }
227 
229 {
230  ASSERT_(grid_);
231 
232  const int cxMax = grid_->getSizeX() - 1;
233  const int cyMax = grid_->getSizeY() - 1;
234 
235  for (int cx = 1; cx < cxMax; cx++)
236  {
237  for (int cy = 1; cy < cyMax; cy++)
238  {
239  auto* thisCell = grid_->cellByIndex(cx, cy);
240  if (*thisCell != CELL_OCCUPIED) continue;
241  // it's occupied:
242  // reset to unknown if no other neighbors is occupied:
243  bool anyNN =
244  (*grid_->cellByIndex(cx - 1, cy - 1) == CELL_OCCUPIED) ||
245  (*grid_->cellByIndex(cx - 1, cy + 0) == CELL_OCCUPIED) ||
246  (*grid_->cellByIndex(cx - 1, cy + 1) == CELL_OCCUPIED) ||
247  (*grid_->cellByIndex(cx + 0, cy - 1) == CELL_OCCUPIED) ||
248  (*grid_->cellByIndex(cx + 0, cy + 1) == CELL_OCCUPIED) ||
249  (*grid_->cellByIndex(cx + 1, cy - 1) == CELL_OCCUPIED) ||
250  (*grid_->cellByIndex(cx + 1, cy + 0) == CELL_OCCUPIED) ||
251  (*grid_->cellByIndex(cx + 1, cy + 1) == CELL_OCCUPIED);
252 
253  if (!anyNN) *thisCell = CELL_UNDEFINED;
254  }
255  }
256 }
257 
259 {
260  ASSERT_(grid_);
261 
262  // Algorithm:
263  // Heckbert, Paul S (1990). "IV.10: A Seed Fill Algorithm". In Glassner,
264  // Andrew S (ed.). Graphics Gems. Academic Press. pp. 275–277.
265  // https://en.wikipedia.org/wiki/Flood_fill
266 
267  const int cxMax = grid_->getSizeX() - 1;
268  const int cyMax = grid_->getSizeY() - 1;
269 
270  const auto Inside = [&](int x, int y) {
271  if (x < 0 || y < 0) return false;
272  if (x > cxMax || y > cyMax) return false;
273  uint8_t* c = grid_->cellByIndex(x, y);
274  if (!c) return false;
275 
276  return *c == CELL_UNDEFINED;
277  };
278 
279  const auto Set = [&](int x, int y) {
280  if (x < 0 || y < 0) return;
281  if (x > cxMax || y > cyMax) return;
282  uint8_t* c = grid_->cellByIndex(x, y);
283  if (!c) return;
284  *c = CELL_FREE;
285  };
286 
287  const int x0 = 0, y0 = 0; // start pixel for flood fill.
288 
289  /*
290  fn fill(x, y):
291  if not Inside(x, y) then return
292  let s = new empty stack or queue
293  Add (x, y) to s
294  while s is not empty:
295  Remove an (x, y) from s
296  let lx = x
297  while Inside(lx - 1, y):
298  Set(lx - 1, y)
299  lx = lx - 1
300  while Inside(x, y):
301  Set(x, y)
302  x = x + 1
303  scan(lx, x - 1, y + 1, s)
304  scan(lx, x - 1, y - 1, s)
305 
306  fn scan(lx, rx, y, s):
307  let span_added = false
308  for x in lx .. rx:
309  if not Inside(x, y):
310  span_added = false
311  else if not span_added:
312  Add (x, y) to s
313  span_added = true
314  */
315 
316  if (!Inside(x0, y0)) return;
317 
318  struct Coord
319  {
320  Coord() = default;
321  Coord(int X, int Y) : x_(X), y_(Y) {}
322 
323  int x_ = 0, y_ = 0;
324  };
325 
326  std::queue<Coord> s;
327 
328  const auto lambdaScan = [&s, &Inside](int lx, int rx, int y) {
329  bool spanAdded = false;
330  for (int x = lx; x <= rx; x++)
331  {
332  if (!Inside(x, y))
333  {
334  spanAdded = false;
335  }
336  else if (!spanAdded)
337  {
338  s.emplace(x, y);
339  spanAdded = true;
340  }
341  }
342  };
343 
344  s.emplace(x0, y0);
345  while (!s.empty())
346  {
347  auto [x, y] = s.front();
348  s.pop();
349  int lx = x;
350  while (Inside(lx - 1, y))
351  {
352  Set(lx - 1, y);
353  lx--;
354  }
355  while (Inside(x, y))
356  {
357  Set(x, y);
358  x++;
359  }
360  lambdaScan(lx, x - 1, y + 1);
361  lambdaScan(lx, x - 1, y - 1);
362  }
363 }
364 
365 // Detects the outter polygon of the grid, after having been flood filled.
366 mrpt::math::TPolygon2D Shape2p5::internalGridContour() const
367 {
368  ASSERT_(grid_);
369 
370  mrpt::math::TPolygon2D p;
371 
372  const int nx = grid_->getSizeX();
373  const int ny = grid_->getSizeY();
374 
375  const std::vector<std::pair<int, int>> dirs = {
376  // first, straight directions (important!)
377  {+1, 0},
378  {-1, 0},
379  {0, +1},
380  {0, -1},
381  // second, diagonals:
382  {+1, +1},
383  {+1, -1},
384  {-1, +1},
385  {-1, -1},
386  };
387 
388  auto lambdaCellIsBorderSimple = [&](int cx, int cy) {
389  auto* c = grid_->cellByIndex(cx, cy);
390  if (!c) return false;
391 
392  if (*c != CELL_OCCUPIED) return false;
393 
394  // check 4 neighbors:
395  if (auto* cS = grid_->cellByIndex(cx, cy - 1); cS && *cS == CELL_FREE)
396  return true;
397  if (auto* cN = grid_->cellByIndex(cx, cy + 1); cN && *cN == CELL_FREE)
398  return true;
399  if (auto* cE = grid_->cellByIndex(cx + 1, cy); cE && *cE == CELL_FREE)
400  return true;
401  if (auto* cW = grid_->cellByIndex(cx - 1, cy); cW && *cW == CELL_FREE)
402  return true;
403 
404  return false;
405  };
406 
407  auto lambdaStillHasUnexploredNeighbors = [&](int cx, int cy) {
408  // precondition: (cx,cy) is VISITED.
409  // We check 8-neighbors:
410 
411  for (const auto& dir : dirs)
412  {
413  const int ix = dir.first, iy = dir.second;
414  const bool isBorder = lambdaCellIsBorderSimple(cx + ix, cy + iy);
415  if (isBorder) return true;
416  }
417  return false;
418  };
419 
420  auto lambdaCellIsBorder = [&](int cx, int cy, bool considerRevisits) {
421  auto* c = grid_->cellByIndex(cx, cy);
422  if (!c) return false;
423 
424  if (*c == CELL_UNDEFINED) return false;
425  if (*c == CELL_FREE) return false;
426  if (*c == CELL_VISITED)
427  {
428  // only consider it if it still has possible free ways to move
429  // around:
430  if (considerRevisits && lambdaStillHasUnexploredNeighbors(cx, cy))
431  return true;
432  else
433  return false;
434  }
435 
436  // check 4 neighbors:
437  if (auto* cS = grid_->cellByIndex(cx, cy - 1); cS && *cS == CELL_FREE)
438  return true;
439  if (auto* cN = grid_->cellByIndex(cx, cy + 1); cN && *cN == CELL_FREE)
440  return true;
441  if (auto* cE = grid_->cellByIndex(cx + 1, cy); cE && *cE == CELL_FREE)
442  return true;
443  if (auto* cW = grid_->cellByIndex(cx - 1, cy); cW && *cW == CELL_FREE)
444  return true;
445 
446  return false;
447  };
448 
449  // 1) Look for the first CELL_OCCUPIED cell:
450  int cx = 0, cy = 0;
451  while (*grid_->cellByIndex(cx, cy) != CELL_OCCUPIED)
452  {
453  cx++;
454  if (cx >= nx)
455  {
456  cx = 0;
457  cy++;
458  ASSERT_(cy < ny);
459  }
460  }
461 
462  // 2) Iterate:
463  // - mark current cell as CELL_VISITED, add to polygon.
464  // - Look in 8 neighbors for a CELL_OCCUPIED with a CELL_FREE cell in
465  // one of its 4 main directions.
466  for (;;)
467  {
468  auto* c = grid_->cellByIndex(cx, cy);
469  ASSERT_(c);
470  *c = CELL_VISITED;
471 
472  // save into polygon too:
473  p.emplace_back(grid_->idx2x(cx), grid_->idx2y(cy));
474 
475  bool cellDone = false;
476 
477  for (int pass = 0; pass < 2 && !cellDone; pass++)
478  {
479  for (const auto& dir : dirs)
480  {
481  const int ix = dir.first, iy = dir.second;
482  const bool isBorder =
483  lambdaCellIsBorder(cx + ix, cy + iy, pass == 1);
484 
485  if (isBorder)
486  {
487 #ifdef DEBUG_DUMP_ALL_TEMPORARY_GRIDS
489 #endif
490  // Save for next iter:
491  cellDone = true;
492  cx = cx + ix;
493  cy = cy + iy;
494  break;
495  }
496  }
497  }
498  if (!cellDone) break;
499  }
500 
501  return p;
502 }
503 
505  const mrpt::math::TPolygon2D& rawGridContour,
506  const std::string& debugStr) const
507 {
508  mrpt::opengl::COpenGLScene scene;
509 
510  auto glGrid = mrpt::opengl::CTexturedPlane::Create();
511  glGrid->setPlaneCorners(
512  grid_->getXMin(), grid_->getXMax(), grid_->getYMin(), grid_->getYMax());
513 
514  mrpt::math::CMatrixDouble mat;
515  grid_->getAsMatrix(mat);
516 
517  mrpt::img::CImage im;
518  im.setFromMatrix(mat, false /* matrix is [0,255]*/);
519 
520  glGrid->assignImage(im);
521 
522  scene.insert(mrpt::opengl::stock_objects::CornerXYZSimple());
523  scene.insert(glGrid);
524 
525  auto lambdaRenderPoly = [&scene](
526  const mrpt::math::TPolygon2D& p,
527  const mrpt::img::TColor& color, double z) {
528  auto glPts = mrpt::opengl::CPointCloud::Create();
529  auto glPoly = mrpt::opengl::CSetOfLines::Create();
530  glPoly->setColor_u8(color);
531  glPts->setColor_u8(color);
532  glPts->setPointSize(4.0f);
533  const auto N = p.size();
534  for (size_t j = 0; j < N; j++)
535  {
536  const size_t j1 = (j + 1) % N;
537  const auto& p0 = p.at(j);
538  const auto& p1 = p.at(j1);
539  glPoly->appendLine(
540  p0.x, p0.y, z + 1e-4 * j, p1.x, p1.y, z + 1e-4 * (j + 1));
541  glPts->insertPoint(p0.x, p0.y, z + 1e-4 * j);
542  }
543  scene.insert(glPoly);
544  scene.insert(glPts);
545  };
546 
547  lambdaRenderPoly(*contour_, {0xff, 0x00, 0x00}, 0.10);
548  lambdaRenderPoly(rawGridContour, {0x00, 0xff, 0x00}, 0.05);
549 
550  if (!debugStr.empty()) scene.getViewport()->addTextMessage(5, 5, debugStr);
551 
552  static int i = 0;
553  scene.saveToFile(mrpt::format("collision_grid_%05i.3Dscene", i++));
554 }
555 
556 std::optional<Shape2p5::RemovalCandidate> Shape2p5::lossOfRemovingVertex(
557  size_t i, const mrpt::math::TPolygon2D& p, bool allowApproxEdges) const
558 {
559  // 1st: check if removing that vertex leads to edges crossing
560  // CELL_UNDEFINED or CELL_OCCUPIED cells:
561 
562  size_t im1 = i > 0 ? i - 1 : (p.size() - 1);
563  size_t ip1 = i == (p.size() - 1) ? 0 : i + 1;
564 
565  const auto& pt_im1 = p[im1];
566  const auto& pt_ip1 = p[ip1];
567  const auto delta = pt_ip1 - pt_im1;
568  const size_t nSteps =
569  static_cast<size_t>(ceil(delta.norm() / grid_->getResolution()));
570  const auto d = delta * (1.0 / nSteps);
571  for (size_t k = 0; k < nSteps; k++)
572  {
573  const auto pt = pt_im1 + d * k;
574  const auto* c = grid_->cellByPos(pt.x, pt.y);
575  if (!c) return {}; // should never happen (!)
576 
577  if (!allowApproxEdges)
578  {
579  // removing this vertex leads to unacceptable approximation:
580  if (*c == CELL_UNDEFINED || *c == CELL_OCCUPIED) return {};
581  }
582  }
583 
584  // ok, removing the vertex is ok.
585  // now, let's quantify the increase in area ("loss"):
587  rc.next = p;
588  rc.next.erase(rc.next.begin() + i);
589 
590  const double originalArea = std::abs(mrpt::math::signedArea(p));
591  const double newArea = std::abs(mrpt::math::signedArea(rc.next));
592  rc.loss = newArea - originalArea;
593 
594  if (allowApproxEdges) rc.loss = -rc.loss;
595 
596  return rc;
597 }
598 
599 mrpt::math::TPolygon2D Shape2p5::internalPrunePolygon(
600  const mrpt::math::TPolygon2D& poly) const
601 {
602  using namespace std::string_literals;
603 
604  mrpt::math::TPolygon2D p = poly;
605 
606  // Algorithm:
607  // Pass #1: go thru all vertices, and pick the one that minimizes
608  // the increase of polygon area while not crossing through any grid cell
609  // that is either CELL_UNDEFINED or CELL_OCCUPIED.
610  // Pass #2: idem, but allow crossing cells.
611  for (int pass = 0; pass < 2; pass++)
612  {
613  while (p.size() > b2_maxPolygonVertices)
614  {
615  std::optional<RemovalCandidate> best;
616 
617  for (size_t i = 0; i < p.size(); i++)
618  {
619  std::optional<RemovalCandidate> rc =
620  lossOfRemovingVertex(i, p, pass == 1);
621  if (rc && (!best || rc->loss < best->loss)) best = *rc;
622  }
623 
624  if (!best) break; // No more vertices found to remove
625 
626  p = best->next;
627 
628 #ifdef DEBUG_DUMP_ALL_TEMPORARY_GRIDS
630  p, mrpt::format("pass #%i loss=%f", pass, best->loss));
631 #endif
632  }
633  }
634 
635  return p;
636 }
637 
638 void Shape2p5::clipZMin(float v)
639 {
640  if (zMin_ < v) zMin_ = v;
641 }
642 
643 void Shape2p5::clipZMax(float v)
644 {
645  if (zMax_ > v) zMax_ = v;
646 }
d
std::optional< mrpt::math::TPolygon2D > contour_
Definition: Shape2p5.h:58
std::optional< SimpleOccGrid > grid_
Definition: Shape2p5.h:75
const mrpt::math::TPolygon2D & getContour() const
Definition: Shape2p5.cpp:95
f
mrpt::math::TPolygon2D internalPrunePolygon(const mrpt::math::TPolygon2D &poly) const
Definition: Shape2p5.cpp:599
constexpr uint8_t CELL_OCCUPIED
Definition: Shape2p5.cpp:38
XmlRpcServer s
void clipZMin(float v)
Definition: Shape2p5.cpp:638
void internalGridFilterSpurious() const
Definition: Shape2p5.cpp:228
geometry_msgs::TransformStamped t
void setShapeManual(const mrpt::math::TPolygon2D &contour, const float zMin, const float zMax)
Definition: Shape2p5.cpp:219
constexpr uint8_t CELL_FREE
Definition: Shape2p5.cpp:39
std::optional< RemovalCandidate > lossOfRemovingVertex(size_t i, const mrpt::math::TPolygon2D &p, bool allowApproxEdges) const
Definition: Shape2p5.cpp:556
constexpr uint8_t CELL_VISITED
Definition: Shape2p5.cpp:40
void computeShape() const
Computes contour_ from the contents in grid_.
Definition: Shape2p5.cpp:170
void buildAddPoint(const mrpt::math::TPoint3Df &pt)
Definition: Shape2p5.cpp:127
double volume() const
Definition: Shape2p5.cpp:42
float zMin() const
Definition: Shape2p5.h:51
mrpt::math::TPolygon2D internalGridContour() const
Definition: Shape2p5.cpp:366
unsigned int step
float zMax() const
Definition: Shape2p5.h:52
mrpt::math::TPolygon2D next
Definition: Shape2p5.h:89
void buildAddTriangle(const mrpt::opengl::TTriangle &t)
Definition: Shape2p5.cpp:136
#define b2_maxPolygonVertices
Definition: b2_settings.h:53
void mergeWith(const Shape2p5 &s)
Definition: Shape2p5.cpp:48
constexpr uint8_t CELL_UNDEFINED
Definition: Shape2p5.cpp:37
void debugSaveGridTo3DSceneFile(const mrpt::math::TPolygon2D &rawGridContour, const std::string &debugStr={}) const
Definition: Shape2p5.cpp:504
void clipZMax(float v)
Definition: Shape2p5.cpp:643
void internalGridFloodFill() const
Definition: Shape2p5.cpp:258
void buildInit(const mrpt::math::TPoint2Df &bbMin, const mrpt::math::TPoint2Df &bbMax, int numCells=100)
Definition: Shape2p5.cpp:106


mvsim
Author(s):
autogenerated on Tue Jul 4 2023 03:08:21