1+ //
2+ // Created by rgrandia on 12.10.21.
3+ //
4+
5+ #include " convex_plane_decomposition/SegmentedPlaneProjection.h"
6+
7+ #include " convex_plane_decomposition/GeometryUtils.h"
8+
9+ namespace convex_plane_decomposition {
10+
11+ namespace { // Helper functions that only make sense in this context
12+
13+ double distanceCost (const Eigen::Vector3d& query, const Eigen::Vector3d& terrainPoint) {
14+ const double dx = query.x () - terrainPoint.x ();
15+ const double dy = query.y () - terrainPoint.y ();
16+ const double dz = query.z () - terrainPoint.z ();
17+ return dx * dx + dy * dy + dz * dz;
18+ }
19+
20+ double distanceCostLowerbound (double distanceSquared) {
21+ return distanceSquared;
22+ }
23+
24+ double intervalSquareDistance (double value, double min, double max) {
25+ // - | 0 | +
26+ // min max
27+ // Returns 0.0 if between min and max. Returns the distance to one boundary otherwise.
28+ if (value < min) {
29+ double diff = min - value;
30+ return diff * diff;
31+ } else if (value < max) {
32+ return 0.0 ;
33+ } else {
34+ double diff = max - value;
35+ return diff * diff;
36+ }
37+ }
38+
39+ double squaredDistanceToBoundingBox (const CgalPoint2d& point, const CgalBbox2d& boundingBox) {
40+ const double dxdx = intervalSquareDistance (point.x (), boundingBox.xmin (), boundingBox.xmax ());
41+ const double dydy = intervalSquareDistance (point.y (), boundingBox.ymin (), boundingBox.ymax ());
42+ return dxdx + dydy;
43+ }
44+
45+ const CgalPolygonWithHoles2d* findInsetContainingThePoint (const CgalPoint2d& point, const std::vector<CgalPolygonWithHoles2d>& insets) {
46+ for (const auto & inset : insets) {
47+ if (isInside (point, inset.outer_boundary ())) {
48+ return &inset;
49+ }
50+ }
51+ return nullptr ;
52+ }
53+
54+ } // namespace
55+
56+ CgalPoint2d projectToPlanarRegion (const CgalPoint2d& queryProjectedToPlane, const PlanarRegion& planarRegion) {
57+ // First search if the projected point is inside any of the insets.
58+ // Note: we know that all insets are non-overlapping, and are not nested (no shape is contained in the hole of another shape)
59+ const auto * const insetPtrContainingPoint = findInsetContainingThePoint (queryProjectedToPlane, planarRegion.boundaryWithInset .insets );
60+
61+ // Compute the projection
62+ CgalPoint2d projectedPoint;
63+ if (insetPtrContainingPoint == nullptr ) {
64+ // Not inside any of the insets. Need to look for the closest one. The projection will be to the boundary
65+ double minDistSquared = std::numeric_limits<double >::max ();
66+ for (const auto & inset : planarRegion.boundaryWithInset .insets ) {
67+ const auto closestPoint = projectToClosestPoint (queryProjectedToPlane, inset.outer_boundary ());
68+ double distSquare = squaredDistance (closestPoint, queryProjectedToPlane);
69+ if (distSquare < minDistSquared) {
70+ projectedPoint = closestPoint;
71+ minDistSquared = distSquare;
72+ }
73+ }
74+ } else {
75+ // Query point is inside and does not need projection...
76+ projectedPoint = queryProjectedToPlane;
77+
78+ // ... unless it is inside a hole
79+ for (const auto & hole : insetPtrContainingPoint->holes ()) {
80+ if (isInside (queryProjectedToPlane, hole)) {
81+ projectedPoint = projectToClosestPoint (queryProjectedToPlane, hole);
82+ break ; // No need to search other holes. Holes are not overlapping
83+ }
84+ }
85+ }
86+
87+ return projectedPoint;
88+ }
89+
90+ std::vector<RegionSortingInfo> sortWithBoundingBoxes (const Eigen::Vector3d& positionInWorld,
91+ const std::vector<convex_plane_decomposition::PlanarRegion>& planarRegions) {
92+ // Compute distance to bounding boxes
93+ std::vector<RegionSortingInfo> regionsAndBboxSquareDistances;
94+ regionsAndBboxSquareDistances.reserve (planarRegions.size ());
95+ for (const auto & planarRegion : planarRegions) {
96+ const Eigen::Vector3d positionInTerrainFrame = planarRegion.transformPlaneToWorld .inverse () * positionInWorld;
97+ const double dzdz = positionInTerrainFrame.z () * positionInTerrainFrame.z ();
98+
99+ RegionSortingInfo regionSortingInfo;
100+ regionSortingInfo.regionPtr = &planarRegion;
101+ regionSortingInfo.positionInTerrainFrame = {positionInTerrainFrame.x (), positionInTerrainFrame.y ()};
102+ regionSortingInfo.boundingBoxSquareDistance =
103+ squaredDistanceToBoundingBox (regionSortingInfo.positionInTerrainFrame , planarRegion.bbox2d ) + dzdz;
104+
105+ regionsAndBboxSquareDistances.push_back (regionSortingInfo);
106+ }
107+
108+ // Sort regions close to far
109+ std::sort (regionsAndBboxSquareDistances.begin (), regionsAndBboxSquareDistances.end (),
110+ [](const RegionSortingInfo& lhs, const RegionSortingInfo& rhs) {
111+ return lhs.boundingBoxSquareDistance < rhs.boundingBoxSquareDistance ;
112+ });
113+
114+ return regionsAndBboxSquareDistances;
115+ }
116+
117+ PlanarTerrainProjection getBestPlanarRegionAtPositionInWorld (const Eigen::Vector3d& positionInWorld,
118+ const std::vector<PlanarRegion>& planarRegions,
119+ const std::function<double (const Eigen::Vector3d&)>& penaltyFunction) {
120+ const auto sortedRegions = sortWithBoundingBoxes (positionInWorld, planarRegions);
121+
122+ // Look for closest planar region.
123+ PlanarTerrainProjection projection;
124+ projection.cost = std::numeric_limits<double >::max ();
125+ for (const auto & regionInfo : sortedRegions) {
126+ // Skip based on lower bound
127+ if (distanceCostLowerbound (regionInfo.boundingBoxSquareDistance ) > projection.cost ) {
128+ continue ;
129+ }
130+
131+ // Project onto planar region
132+ const auto projectedPointInTerrainFrame = projectToPlanarRegion (regionInfo.positionInTerrainFrame , *regionInfo.regionPtr );
133+
134+ // Express projected point in World frame
135+ const auto projectionInWorldFrame =
136+ positionInWorldFrameFromPosition2dInPlane (projectedPointInTerrainFrame, regionInfo.regionPtr ->transformPlaneToWorld );
137+
138+ const auto cost = distanceCost (positionInWorld, projectionInWorldFrame) + penaltyFunction (projectionInWorldFrame);
139+ if (cost < projection.cost ) {
140+ projection.cost = cost;
141+ projection.regionPtr = regionInfo.regionPtr ;
142+ projection.positionInTerrainFrame = projectedPointInTerrainFrame;
143+ projection.positionInWorld = projectionInWorldFrame;
144+ }
145+ }
146+
147+ return projection;
148+ }
149+
150+ } // namespace convex_plane_decomposition
0 commit comments