Skip to content

Commit

Permalink
Refactor make box mesh code (RobotLocomotion#12998)
Browse files Browse the repository at this point in the history
If the make_box_mesh.h file is included into multiple compilation units
all of the in-header functions get compiled into each unit. Many of the
functions are declared `static` so they don't collide. However, one is not
and that actually causes linker-errors.

Rather than just declaring the single offending function static/inline,
this moves the implementation into a .cc file to improve overall
compilation time. There are three functions that only exist to serve the
implementation of the mesh generation. Their declarations have been kept
in the header file because they are directly tested in make_box_mesh_test.

Also, the function comments were changed from /** */ to /* */ reflecting
their non-doxygen, internal:: status.

Otherwise, this introduces no new code and doesn't change the text of any
comments.
  • Loading branch information
SeanCurtis-TRI authored Apr 6, 2020
1 parent b92b1f6 commit 7cf4c20
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 170 deletions.
1 change: 1 addition & 0 deletions geometry/proximity/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ drake_cc_library(

drake_cc_library(
name = "make_box_mesh",
srcs = ["make_box_mesh.cc"],
hdrs = ["make_box_mesh.h"],
deps = [
":surface_mesh",
Expand Down
138 changes: 138 additions & 0 deletions geometry/proximity/make_box_mesh.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include "drake/geometry/proximity/make_box_mesh.h"

namespace drake {
namespace geometry {
namespace internal {

int CalcSequentialIndex(int i, int j, int k, const Vector3<int>& num_vertices) {
DRAKE_DEMAND(0 <= i && i < num_vertices.x());
DRAKE_DEMAND(0 <= j && j < num_vertices.y());
DRAKE_DEMAND(0 <= k && k < num_vertices.z());
return i * num_vertices.y() * num_vertices.z() + j * num_vertices.z() + k;
}

template <typename T>
std::vector<VolumeVertex<T>> GenerateVertices(
const Box& box, const Vector3<int>& num_vertices) {
const T half_x = box.width() / T(2);
const T half_y = box.depth() / T(2);
const T half_z = box.height() / T(2);
const auto x_coords =
VectorX<T>::LinSpaced(num_vertices.x(), -half_x, half_x);
const auto y_coords =
VectorX<T>::LinSpaced(num_vertices.y(), -half_y, half_y);
const auto z_coords =
VectorX<T>::LinSpaced(num_vertices.z(), -half_z, half_z);

std::vector<VolumeVertex<T>> vertices;
vertices.reserve(num_vertices.x() * num_vertices.y() * num_vertices.z());
// The order of nested i-loop, j-loop, then k-loop makes the sequence of
// vertices consistent with CalcSequentialIndex.
for (int i = 0; i < num_vertices.x(); ++i) {
for (int j = 0; j < num_vertices.y(); ++j) {
for (int k = 0; k < num_vertices.z(); ++k) {
vertices.emplace_back(x_coords[i], y_coords[j], z_coords[k]);
}
}
}
return vertices;
}

void AddSixTetrahedraOfCell(const Vector3<int>& lowest,
const Vector3<int>& num_vertices,
std::vector<VolumeElement>* elements) {
const int i = lowest.x();
const int j = lowest.y();
const int k = lowest.z();
// Get the sequential indices of the eight vertices of the rectangular cell.
int v[8];
int s = 0;
for (int l = 0; l < 2; ++l)
for (int m = 0; m < 2; ++m)
for (int n = 0; n < 2; ++n)
v[s++] = CalcSequentialIndex(i + l, j + m, k + n, num_vertices);
// The following picture shows where vertex vₛ (for `v[s]` above) locates
// in the rectangular cell. The I-, J-, K-axes show the direction of
// increasing i, j, k indices.
//
// v₁ v₃
// ●------●
// /| /|
// / | v₇/ |
// v₅ ●------● |
// | | | |
// | ●---|--● v₂
// | /v₀ | /
// |/ |/
// +K v₄ ●------● v₆
// |
// |
// o------+J
// /
// /
// +I
//
// The following table subdivides the rectangular cell into six tetrahedra.
// Refer to the picture above to determine which four vertices form a
// tetrahedron. The six tetrahedra form a cycle around the diagonal v₀v₇
// of the cell. Refer to:
// http://www.baumanneduard.ch/Splitting%20a%20cube%20in%20tetrahedras2.htm
const int tetrahedron[6][4]{
// clang-format off
{v[0], v[7], v[4], v[6]},
{v[0], v[7], v[6], v[2]},
{v[0], v[7], v[2], v[3]},
{v[0], v[7], v[3], v[1]},
{v[0], v[7], v[1], v[5]},
{v[0], v[7], v[5], v[4]}};
// clang-format on
// The above table guarantees that adjacent rectangular cells will be
// subdivided in a consistent way, i.e., both cells will pick the same
// diagonal of their shared rectangular face.
for (int t = 0; t < 6; ++t) elements->emplace_back(tetrahedron[t]);
}

std::vector<VolumeElement> GenerateElements(const Vector3<int>& num_vertices) {
std::vector<VolumeElement> elements;
const Vector3<int> num_cell = num_vertices - Vector3<int>(1, 1, 1);
elements.reserve(6 * num_cell.x() * num_cell.y() * num_cell.z());
for (int i = 0; i < num_cell.x(); ++i) {
for (int j = 0; j < num_cell.y(); ++j) {
for (int k = 0; k < num_cell.z(); ++k) {
AddSixTetrahedraOfCell(Vector3<int>(i, j, k), num_vertices, &elements);
}
}
}
return elements;
}

template <typename T>
VolumeMesh<T> MakeBoxVolumeMesh(const Box& box, double resolution_hint) {
// TODO(DamrongGuoy): Generate the mesh with rigid core at medial axis or
// offset surface (issue #11906) and remove the "@note" above.
DRAKE_DEMAND(resolution_hint > 0.);
// Number of vertices in x-, y-, and z- directions. In each direction,
// there is one more vertices than cells.
const Vector3<int> num_vertices{
1 + static_cast<int>(ceil(box.width() / resolution_hint)),
1 + static_cast<int>(ceil(box.depth() / resolution_hint)),
1 + static_cast<int>(ceil(box.height() / resolution_hint))};

std::vector<VolumeVertex<T>> vertices =
GenerateVertices<T>(box, num_vertices);

std::vector<VolumeElement> elements = GenerateElements(num_vertices);

return VolumeMesh<T>(std::move(elements), std::move(vertices));
}

// Explicit instantiations for double and AutoDiffXd to support the
// documentation.
template VolumeMesh<double> MakeBoxVolumeMesh(const Box& box,
double resolution_hint);
template VolumeMesh<AutoDiffXd> MakeBoxVolumeMesh(const Box& box,
double resolution_hint);

} // namespace internal
} // namespace geometry
} // namespace drake
Loading

0 comments on commit 7cf4c20

Please sign in to comment.