You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
7.0 KiB
177 lines
7.0 KiB
#ifndef MEDUSA_BITS_DOMAINS_POLYHEDRONSHAPE_FWD_HPP_
|
|
#define MEDUSA_BITS_DOMAINS_POLYHEDRONSHAPE_FWD_HPP_
|
|
|
|
/**
|
|
* @file
|
|
* Implementation of shape representing polyhedrons.
|
|
*
|
|
* @example test/domains/PolyhedronShape_test.cpp
|
|
*/
|
|
|
|
#include <medusa/Config.hpp>
|
|
#include <medusa/bits/domains/discretization_helpers.hpp>
|
|
#include <medusa/bits/domains/discretization_helpers_advanced.hpp>
|
|
#include <medusa/bits/domains/GeneralFill.hpp>
|
|
#include <medusa/bits/domains/DomainShape.hpp>
|
|
#include <medusa/bits/domains/PolytopeShape.hpp>
|
|
#include <medusa/bits/types/Vec.hpp>
|
|
|
|
#include <CGAL/AABB_face_graph_triangle_primitive.h>
|
|
#include <CGAL/AABB_traits.h>
|
|
#include <CGAL/AABB_tree.h>
|
|
#include <CGAL/Side_of_triangle_mesh.h>
|
|
#include <CGAL/Simple_cartesian.h>
|
|
#include <CGAL/Surface_mesh.h>
|
|
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
|
|
|
#include <fstream>
|
|
|
|
namespace mm {
|
|
|
|
/**
|
|
* A polyhedron represented by a closed triangular mesh. This class uses CGAL
|
|
* (https://www.cgal.org/) and its `Surface_mesh` class to do the heavy lifting.
|
|
* @note CGAL (libcgal-dev) must be installed on your system and available in include and link
|
|
* paths for this class to work.
|
|
* @warning This header must be explicitly included to bet the 3D PolyhedronShape functionality. It is
|
|
* not included by default due to its dependency on CGAL.
|
|
*
|
|
* @tparam vec_t This shape class is used only for 3D domains.
|
|
*
|
|
* Usage example:
|
|
* @snippet domains/PolyhedronShape_test.cpp PolyhedronShape usage example
|
|
* @ingroup domains
|
|
*/
|
|
template <typename vec_t>
|
|
class PolyhedronShape : public DomainShape<vec_t> {
|
|
static_assert(vec_t::dim == 3, "Only available in 3 dimensions.");
|
|
using base_t = DomainShape<vec_t>; ///< Base class type.
|
|
|
|
/* CGAL types */
|
|
typedef CGAL::Simple_cartesian<double> K; ///< CGAL geometry kernel.
|
|
typedef K::Point_3 Point; ///< CGAL point type.
|
|
typedef K::Vector_3 Vector; ///< CGAL vector type.
|
|
typedef CGAL::Surface_mesh<Point> Mesh; ///< CGAL surface mesh type.
|
|
typedef Mesh::Vertex_index VI; ///< CGAL index type for a point on a surface mesh.
|
|
typedef Mesh::Face_index FI; ///< CGAL index type for a face on a surface mesh.
|
|
/// CGAL type for storing triangular faces of surface meshes in AABB tree.
|
|
typedef CGAL::AABB_face_graph_triangle_primitive<Mesh> Primitive;
|
|
/// CGAL type for axis-aligned bounding-box trees, used for testing point inclusions.
|
|
typedef CGAL::AABB_tree<CGAL::AABB_traits<K, Primitive>> Tree;
|
|
/// CGAL type of the function for testing point inclusion.
|
|
typedef CGAL::Side_of_triangle_mesh<Mesh, K> PointInside;
|
|
|
|
Mesh surface; ///< Surface of the polyhedron represented as a triangular mesh.
|
|
Tree tree; ///< AABB tree of the faces.
|
|
PointInside inside_tester; ///< Function used for testing point inclusions.
|
|
|
|
/// Initialize the tree and compute surface normals.
|
|
void init() {
|
|
tree.insert(surface.faces_begin(), surface.faces_end(), surface);
|
|
tree.accelerate_distance_queries();
|
|
|
|
auto vnormals = surface.add_property_map<VI, Vector>("v:normals", CGAL::NULL_VECTOR).first;
|
|
auto fnormals = surface.add_property_map<FI, Vector>("f:normals", CGAL::NULL_VECTOR).first;
|
|
CGAL::Polygon_mesh_processing::compute_normals(surface, vnormals, fnormals);
|
|
}
|
|
|
|
public:
|
|
using typename base_t::scalar_t;
|
|
using typename base_t::vector_t;
|
|
using base_t::dim;
|
|
using base_t::discretizeBoundaryWithDensity;
|
|
using base_t::discretizeBoundaryWithStep;
|
|
|
|
|
|
/// Construct a shape from a CGAL surface mesh. @sa fromOff.
|
|
explicit PolyhedronShape(const Mesh& surface)
|
|
: surface(surface), tree(), inside_tester(tree) { init(); }
|
|
|
|
/// Copy constructor (necessary to rebuild the tree).
|
|
PolyhedronShape(const PolyhedronShape& other)
|
|
: surface(other.surface), tree(), inside_tester(tree) { init(); }
|
|
|
|
/// Copy assignment (necessary to rebuild the tree).
|
|
PolyhedronShape& operator=(const PolyhedronShape& other) {
|
|
surface = other.surface;
|
|
tree.clear();
|
|
init();
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Read a triangular closed surface mesh describing a polyhedron from a `.off` file.
|
|
* The face colors are read and stored as well and used to determine surface point types.
|
|
* See the method @ref rgb2type for how RBG colors are converted to boundary types.
|
|
* @param filename Path to the `.off` file.
|
|
* @throws Assertion fails if file cannot be read, the surface is empty, not triangular or not
|
|
* closed.
|
|
*/
|
|
static PolyhedronShape fromOFF(const std::string& filename) {
|
|
std::ifstream input(filename);
|
|
assert_msg(input, "Failed opening file %s: %s", filename, strerror(errno));
|
|
Mesh surface;
|
|
input >> surface;
|
|
assert_msg(!input.fail(), "Invalid OFF file - either parsing failed or the data "
|
|
"does not represent a two-manifold.");
|
|
assert_msg(input, "Failed reading file %s: %s", filename, strerror(errno));
|
|
assert_msg(!surface.is_empty(), "Surface is empty.");
|
|
assert_msg(CGAL::is_triangle_mesh(surface), "Surface must be a triangle mesh.");
|
|
assert_msg(CGAL::is_closed(surface), "Surface must be closed (without holes).");
|
|
return PolyhedronShape(surface);
|
|
}
|
|
|
|
bool contains(const vec_t& point) const override {
|
|
return inside_tester(Point(point[0], point[1], point[2])) != CGAL::ON_UNBOUNDED_SIDE;
|
|
}
|
|
|
|
std::pair<vec_t, vec_t> bbox() const override {
|
|
auto b = tree.bbox();
|
|
return {{b.xmin(), b.ymin(), b.zmin()}, {b.xmax(), b.ymax(), b.zmax()}};
|
|
}
|
|
|
|
DomainDiscretization<vec_t> discretizeBoundaryWithDensity(
|
|
const std::function<scalar_t(vec_t)>& dr, int type) const override;
|
|
|
|
std::ostream& print(std::ostream& os) const override {
|
|
return os << "PolyhedronShape with " << surface.num_vertices() << " vertices"
|
|
<< " and " << surface.num_faces() << " faces.";
|
|
}
|
|
|
|
PolyhedronShape* clone() const override { return new PolyhedronShape(*this); }
|
|
|
|
/// Convert a RBG color to an integer value that can be used as a boundary type.
|
|
static int rgb2type(uint8_t r, uint8_t g, uint8_t b) {
|
|
return -((static_cast<int>(r) << 16) + (static_cast<int>(g) << 8) + b) - 1;
|
|
}
|
|
|
|
private:
|
|
/// Convert a CGAL vertex index to a Medusa vector.
|
|
vec_t getPoint(VI idx) const {
|
|
auto p = surface.point(idx);
|
|
return {p.x(), p.y(), p.z()};
|
|
}
|
|
};
|
|
|
|
// This must be hidden during a doxygen run due to a bug in processing const declarations with
|
|
// differently named template parameters.
|
|
// See https://github.com/doxygen/doxygen/issues/8178
|
|
#ifndef DOXYGEN_RUN
|
|
/**
|
|
* Specialization for 3D, implementing polyhedrons.
|
|
* @sa PolyhedronShape
|
|
* @ingroup domains
|
|
*/
|
|
template <typename Scalar>
|
|
class PolytopeShape<Vec<Scalar, 3>> : public PolyhedronShape<Vec<Scalar, 3>> {
|
|
using base_t = PolyhedronShape<Vec<Scalar, 3>>;
|
|
using typename base_t::PolyhedronShape;
|
|
public:
|
|
PolytopeShape(const base_t& shape) : base_t(shape) {}
|
|
};
|
|
#endif
|
|
|
|
|
|
} // namespace mm
|
|
|
|
#endif // MEDUSA_BITS_DOMAINS_POLYHEDRONSHAPE_FWD_HPP_
|
|
|