Program Listing for File traversal_node_hfield_shape.h
↰ Return to documentation for file (/tmp/ws/src/hpp-fcl/include/hpp/fcl/internal/traversal_node_hfield_shape.h
)
/*
* Software License Agreement (BSD License)
*
* Copyright (c) 2021, INRIA.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Open Source Robotics Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HPP_FCL_TRAVERSAL_NODE_HFIELD_SHAPE_H
#define HPP_FCL_TRAVERSAL_NODE_HFIELD_SHAPE_H
#include <hpp/fcl/collision_data.h>
#include <hpp/fcl/shape/geometric_shapes.h>
#include <hpp/fcl/narrowphase/narrowphase.h>
#include <hpp/fcl/shape/geometric_shapes_utility.h>
#include <hpp/fcl/internal/traversal_node_base.h>
#include <hpp/fcl/internal/traversal.h>
#include <hpp/fcl/hfield.h>
#include <hpp/fcl/shape/convex.h>
namespace hpp {
namespace fcl {
namespace details {
template <typename BV>
Convex<Quadrilateral> buildConvexQuadrilateral(const HFNode<BV>& node,
const HeightField<BV>& model) {
const MatrixXf& heights = model.getHeights();
const VecXf& x_grid = model.getXGrid();
const VecXf& y_grid = model.getYGrid();
const FCL_REAL min_height = model.getMinHeight();
const FCL_REAL x0 = x_grid[node.x_id], x1 = x_grid[node.x_id + 1],
y0 = y_grid[node.y_id], y1 = y_grid[node.y_id + 1];
const Eigen::Block<const MatrixXf, 2, 2> cell =
heights.block<2, 2>(node.y_id, node.x_id);
assert(cell.maxCoeff() > min_height &&
"max_height is lower than min_height"); // Check whether the geometry
// is degenerated
Vec3f* pts = new Vec3f[8];
pts[0] = Vec3f(x0, y0, min_height);
pts[1] = Vec3f(x0, y1, min_height);
pts[2] = Vec3f(x1, y1, min_height);
pts[3] = Vec3f(x1, y0, min_height);
pts[4] = Vec3f(x0, y0, cell(0, 0));
pts[5] = Vec3f(x0, y1, cell(1, 0));
pts[6] = Vec3f(x1, y1, cell(1, 1));
pts[7] = Vec3f(x1, y0, cell(0, 1));
Quadrilateral* polygons = new Quadrilateral[6];
polygons[0].set(0, 3, 2, 1); // x+ side
polygons[1].set(0, 1, 5, 4); // y- side
polygons[2].set(1, 2, 6, 5); // x- side
polygons[3].set(2, 3, 7, 6); // y+ side
polygons[4].set(3, 0, 4, 7); // z- side
polygons[5].set(4, 5, 6, 7); // z+ side
return Convex<Quadrilateral>(true,
pts, // points
8, // num points
polygons,
6 // number of polygons
);
}
template <typename BV>
void buildConvexTriangles(const HFNode<BV>& node, const HeightField<BV>& model,
Convex<Triangle>& convex1,
Convex<Triangle>& convex2) {
const MatrixXf& heights = model.getHeights();
const VecXf& x_grid = model.getXGrid();
const VecXf& y_grid = model.getYGrid();
const FCL_REAL min_height = model.getMinHeight();
const FCL_REAL x0 = x_grid[node.x_id], x1 = x_grid[node.x_id + 1],
y0 = y_grid[node.y_id], y1 = y_grid[node.y_id + 1];
const FCL_REAL max_height = node.max_height;
const Eigen::Block<const MatrixXf, 2, 2> cell =
heights.block<2, 2>(node.y_id, node.x_id);
assert(max_height > min_height &&
"max_height is lower than min_height"); // Check whether the geometry
// is degenerated
HPP_FCL_UNUSED_VARIABLE(max_height);
{
Vec3f* pts = new Vec3f[8];
pts[0] = Vec3f(x0, y0, min_height);
pts[1] = Vec3f(x0, y1, min_height);
pts[2] = Vec3f(x1, y1, min_height);
pts[3] = Vec3f(x1, y0, min_height);
pts[4] = Vec3f(x0, y0, cell(0, 0));
pts[5] = Vec3f(x0, y1, cell(1, 0));
pts[6] = Vec3f(x1, y1, cell(1, 1));
pts[7] = Vec3f(x1, y0, cell(0, 1));
Triangle* triangles = new Triangle[8];
triangles[0].set(0, 1, 3); // bottom
triangles[1].set(4, 5, 7); // top
triangles[2].set(0, 1, 4);
triangles[3].set(4, 1, 5);
triangles[4].set(1, 7, 3);
triangles[5].set(1, 5, 7);
triangles[6].set(0, 3, 7);
triangles[7].set(7, 4, 0);
convex1.set(true,
pts, // points
8, // num points
triangles,
8 // number of polygons
);
}
{
Vec3f* pts = new Vec3f[8];
memcpy(pts, convex1.points, 8 * sizeof(Vec3f));
Triangle* triangles = new Triangle[8];
triangles[0].set(3, 2, 1); // top
triangles[1].set(5, 6, 7); // bottom
triangles[2].set(1, 2, 5);
triangles[3].set(5, 2, 6);
triangles[4].set(1, 3, 7);
triangles[5].set(1, 7, 5);
triangles[6].set(2, 3, 7);
triangles[7].set(6, 2, 3);
convex2.set(true,
pts, // points
8, // num points
triangles,
8 // number of polygons
);
}
}
} // namespace details
template <typename BV, typename S,
int _Options = RelativeTransformationIsIdentity>
class HeightFieldShapeCollisionTraversalNode
: public CollisionTraversalNodeBase {
public:
typedef CollisionTraversalNodeBase Base;
typedef Eigen::Array<FCL_REAL, 1, 2> Array2d;
enum {
Options = _Options,
RTIsIdentity = _Options & RelativeTransformationIsIdentity
};
HeightFieldShapeCollisionTraversalNode(const CollisionRequest& request)
: CollisionTraversalNodeBase(request) {
model1 = NULL;
model2 = NULL;
num_bv_tests = 0;
num_leaf_tests = 0;
query_time_seconds = 0.0;
nsolver = NULL;
shape_inflation.setZero();
}
bool isFirstNodeLeaf(unsigned int b) const {
return model1->getBV(b).isLeaf();
}
int getFirstLeftChild(unsigned int b) const {
return static_cast<int>(model1->getBV(b).leftChild());
}
int getFirstRightChild(unsigned int b) const {
return static_cast<int>(model1->getBV(b).rightChild());
}
bool BVDisjoints(unsigned int b1, unsigned int /*b2*/,
FCL_REAL& sqrDistLowerBound) const {
if (this->enable_statistics) this->num_bv_tests++;
bool disjoint;
if (RTIsIdentity) {
assert(false && "must never happened");
disjoint = !this->model1->getBV(b1).bv.overlap(
this->model2_bv, this->request, sqrDistLowerBound);
} else {
disjoint = !overlap(this->tf1.getRotation(), this->tf1.getTranslation(),
this->model1->getBV(b1).bv, this->model2_bv,
this->request, sqrDistLowerBound);
}
if (disjoint)
internal::updateDistanceLowerBoundFromBV(this->request, *this->result,
sqrDistLowerBound);
assert(!disjoint || sqrDistLowerBound > 0);
return disjoint;
}
template <typename Polygone>
bool shapeDistance(const Convex<Polygone>& convex1,
const Convex<Polygone>& convex2, const Transform3f& tf1,
const S& shape, const Transform3f& tf2, FCL_REAL& distance,
Vec3f& c1, Vec3f& c2, Vec3f& normal) const {
const Transform3f Id;
Vec3f contact2_1, contact2_2, normal2;
FCL_REAL distance2;
bool collision1, collision2;
if (RTIsIdentity)
collision1 = !nsolver->shapeDistance(convex1, Id, shape, tf2, distance,
c1, c2, normal);
else
collision1 = !nsolver->shapeDistance(convex1, tf1, shape, tf2, distance,
c1, c2, normal);
if (RTIsIdentity)
collision2 = !nsolver->shapeDistance(convex2, Id, shape, tf2, distance2,
c1, c2, normal);
else
collision2 = !nsolver->shapeDistance(convex2, tf1, shape, tf2, distance2,
contact2_1, contact2_2, normal2);
if (collision1 && collision2) {
if (distance > distance2) // switch values
{
distance = distance2;
c1 = contact2_1;
c2 = contact2_2;
normal = normal2;
}
return true;
} else if (collision1) {
return true;
} else if (collision2) {
distance = distance2;
c1 = contact2_1;
c2 = contact2_2;
normal = normal2;
return true;
}
return false;
}
template <typename Polygone>
bool shapeCollision(const Convex<Polygone>& convex1,
const Convex<Polygone>& convex2, const Transform3f& tf1,
const S& shape, const Transform3f& tf2,
FCL_REAL& distance_lower_bound, Vec3f& contact_point,
Vec3f& normal) const {
const Transform3f Id;
Vec3f contact_point2, normal2;
FCL_REAL distance_lower_bound2;
bool collision1, collision2;
if (RTIsIdentity)
collision1 =
nsolver->shapeIntersect(convex1, Id, shape, tf2, distance_lower_bound,
true, &contact_point, &normal);
else
collision1 = nsolver->shapeIntersect(convex1, tf1, shape, tf2,
distance_lower_bound, true,
&contact_point, &normal);
if (RTIsIdentity)
collision2 = nsolver->shapeIntersect(convex2, Id, shape, tf2,
distance_lower_bound2, true,
&contact_point2, &normal2);
else
collision2 = nsolver->shapeIntersect(convex2, tf1, shape, tf2,
distance_lower_bound2, true,
&contact_point2, &normal2);
if (collision1 && collision2) {
// In some case, EPA might returns something like
// -(std::numeric_limits<FCL_REAL>::max)().
if (distance_lower_bound != -(std::numeric_limits<FCL_REAL>::max)() &&
distance_lower_bound2 != -(std::numeric_limits<FCL_REAL>::max)()) {
if (distance_lower_bound > distance_lower_bound2) // switch values
{
distance_lower_bound = distance_lower_bound2;
contact_point = contact_point2;
normal = normal2;
}
} else if (distance_lower_bound2 !=
-(std::numeric_limits<FCL_REAL>::max)()) {
distance_lower_bound = distance_lower_bound2;
contact_point = contact_point2;
normal = normal2;
}
return true;
} else if (collision1) {
return true;
} else if (collision2) {
distance_lower_bound = distance_lower_bound2;
contact_point = contact_point2;
normal = normal2;
return true;
}
return false;
}
void leafCollides(unsigned int b1, unsigned int /*b2*/,
FCL_REAL& sqrDistLowerBound) const {
if (this->enable_statistics) this->num_leaf_tests++;
const HFNode<BV>& node = this->model1->getBV(b1);
// Split quadrilateral primitives into two convex shapes corresponding to
// polyhedron with triangular bases. This is essential to keep the convexity
// typedef Convex<Quadrilateral> ConvexQuadrilateral;
// const ConvexQuadrilateral convex =
// details::buildConvexQuadrilateral(node,*this->model1);
typedef Convex<Triangle> ConvexTriangle;
ConvexTriangle convex1, convex2;
details::buildConvexTriangles(node, *this->model1, convex1, convex2);
FCL_REAL distance;
// Vec3f contact_point, normal;
Vec3f c1, c2, normal;
bool collision =
this->shapeDistance(convex1, convex2, this->tf1, *(this->model2),
this->tf2, distance, c1, c2, normal);
// this->shapeCollision(convex1, convex2, this->tf1, *(this->model2),
// this->tf2,
// distance, contact_point, normal);
FCL_REAL distToCollision = distance - this->request.security_margin;
if (distToCollision <= this->request.collision_distance_threshold) {
sqrDistLowerBound = 0;
if (this->request.num_max_contacts > this->result->numContacts()) {
this->result->addContact(Contact(this->model1, this->model2, (int)b1,
(int)Contact::NONE, .5 * (c1 + c2),
(c2 - c1).normalized(), -distance));
}
} else if (collision && this->request.security_margin >= 0) {
sqrDistLowerBound = 0;
if (this->request.num_max_contacts > this->result->numContacts()) {
this->result->addContact(Contact(this->model1, this->model2, (int)b1,
(int)Contact::NONE, c1, normal,
-distance));
assert(this->result->isCollision());
}
} else
sqrDistLowerBound = distToCollision * distToCollision;
// const Vec3f c1 = contact_point - distance * 0.5 * normal;
// const Vec3f c2 = contact_point + distance * 0.5 * normal;
internal::updateDistanceLowerBoundFromLeaf(this->request, *this->result,
distToCollision, c1, c2);
assert(this->result->isCollision() || sqrDistLowerBound > 0);
}
const GJKSolver* nsolver;
const HeightField<BV>* model1;
const S* model2;
BV model2_bv;
Array2d shape_inflation;
mutable int num_bv_tests;
mutable int num_leaf_tests;
mutable FCL_REAL query_time_seconds;
};
template <typename BV, typename S,
int _Options = RelativeTransformationIsIdentity>
class HeightFieldShapeDistanceTraversalNode : public DistanceTraversalNodeBase {
public:
typedef DistanceTraversalNodeBase Base;
enum {
Options = _Options,
RTIsIdentity = _Options & RelativeTransformationIsIdentity
};
HeightFieldShapeDistanceTraversalNode() : DistanceTraversalNodeBase() {
model1 = NULL;
model2 = NULL;
num_leaf_tests = 0;
query_time_seconds = 0.0;
rel_err = 0;
abs_err = 0;
nsolver = NULL;
}
bool isFirstNodeLeaf(unsigned int b) const {
return model1->getBV(b).isLeaf();
}
int getFirstLeftChild(unsigned int b) const {
return model1->getBV(b).leftChild();
}
int getFirstRightChild(unsigned int b) const {
return model1->getBV(b).rightChild();
}
FCL_REAL BVDistanceLowerBound(unsigned int b1, unsigned int /*b2*/) const {
return model1->getBV(b1).bv.distance(
model2_bv); // TODO(jcarpent): tf1 is not taken into account here.
}
void leafComputeDistance(unsigned int b1, unsigned int /*b2*/) const {
if (this->enable_statistics) this->num_leaf_tests++;
const BVNode<BV>& node = this->model1->getBV(b1);
typedef Convex<Quadrilateral> ConvexQuadrilateral;
const ConvexQuadrilateral convex =
details::buildConvexQuadrilateral(node, *this->model1);
FCL_REAL d;
Vec3f closest_p1, closest_p2, normal;
nsolver->shapeDistance(convex, this->tf1, *(this->model2), this->tf2, d,
closest_p1, closest_p2, normal);
this->result->update(d, this->model1, this->model2, b1,
DistanceResult::NONE, closest_p1, closest_p2, normal);
}
bool canStop(FCL_REAL c) const {
if ((c >= this->result->min_distance - abs_err) &&
(c * (1 + rel_err) >= this->result->min_distance))
return true;
return false;
}
FCL_REAL rel_err;
FCL_REAL abs_err;
const GJKSolver* nsolver;
const HeightField<BV>* model1;
const S* model2;
BV model2_bv;
mutable int num_bv_tests;
mutable int num_leaf_tests;
mutable FCL_REAL query_time_seconds;
};
} // namespace fcl
} // namespace hpp
#endif