diff --git a/.clang-format b/.clang-format index b73290a5..d513fa49 100644 --- a/.clang-format +++ b/.clang-format @@ -2,9 +2,9 @@ --- Language: Cpp AccessModifierOffset: -2 -AlignAfterOpenBracket: AlwaysBreak -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true AlignEscapedNewlinesLeft: false AlignEscapedNewlines: Left AlignOperands: true @@ -15,11 +15,10 @@ AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: TopLevel AlwaysBreakAfterReturnType: All AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true -BinPackArguments: true +BinPackArguments: false BinPackParameters: false BraceWrapping: AfterClass: true @@ -31,14 +30,15 @@ BraceWrapping: AfterStruct: true AfterUnion: false BeforeCatch: false - BeforeElse: false + BeforeElse: true IndentBraces: false + SplitEmptyFunction: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true +BreakStringLiterals: false ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: true @@ -65,7 +65,7 @@ KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None +NamespaceIndentation: All ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true @@ -76,7 +76,7 @@ PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left -ReflowComments: true +ReflowComments: false SortIncludes: false SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true diff --git a/Examples/AMReX_DCEL/main.cpp b/Examples/AMReX_DCEL/main.cpp index 4dbd2c50..b1a37a3a 100644 --- a/Examples/AMReX_DCEL/main.cpp +++ b/Examples/AMReX_DCEL/main.cpp @@ -16,95 +16,95 @@ using namespace amrex; namespace amrex { -namespace EB2 { - -/*! - @brief This is an AMReX-capable version of the EBGeometry BVH accelerator. It - is templated as T, BV, K which indicate the EBGeometry precision, bounding - volume, and tree degree. -*/ -template -class SignedDistanceBVH -{ -public: - /*! -@brief Alias for builder node, for encapsulating a "standard" BVH node - */ - using BuilderNode = EBGeometry::BVH::NodeT, BV, K>; - - /*! -@brief Alias for linearized BVH node - */ - using LinearNode = EBGeometry::BVH::LinearBVH, BV, K>; - - /*! -@brief Alias for always-3D vector - */ - using Vec3 = EBGeometry::Vec3T; - - /*! -@brief Full constructor. -@param[in] a_filename File name. Must be a PLY file and will be parser by the -PLY parser. -@param[in] a_flipSign Hook for swapping inside/outside. - */ - SignedDistanceBVH(const std::string a_filename, const bool a_flipSign) - { - - // 1. Read mesh from file. - auto mesh = EBGeometry::Parser::PLY::readIntoDCEL(a_filename); - - // 2. Create a standard BVH hierarchy. This is not a compact ree. - auto root = std::make_shared(mesh->getFaces()); - root->topDownSortAndPartitionPrimitives( - EBGeometry::DCEL::defaultBVConstructor, EBGeometry::DCEL::defaultPartitioner, - EBGeometry::DCEL::defaultStopFunction); - - // 3. Flatten the tree onto a tighter memory representation. - m_rootNode = root->flattenTree(); - } - - /*! -@brief Copy constructor. -@param[in] a_other Other SDF. - */ - SignedDistanceBVH(const SignedDistanceBVH& a_other) - { - this->m_rootNode = a_other.m_rootNode; - this->m_flipSign = a_other.m_flipSign; - } - - /*! -@brief AMReX's implicit function definition. - */ - Real operator()(AMREX_D_DECL(Real x, Real y, Real z)) const noexcept - { - const Real sign = (m_flipSign) ? -1.0 : 1.0; - - return sign * m_rootNode->signedDistance(Vec3(x, y, z)); - }; - - /*! -@brief Also an AMReX implicit function implementation - */ - inline Real - operator()(const RealArray& p) const noexcept - { - return this->operator()(AMREX_D_DECL(p[0], p[1], p[2])); - } - -protected: - /*! -@brief Root node of the linearized BVH hierarchy. - */ - std::shared_ptr m_rootNode; - - /*! -@brief Hook for flipping the sign - */ - bool m_flipSign; -}; -} // namespace EB2 + namespace EB2 { + + /*! + @brief This is an AMReX-capable version of the EBGeometry BVH accelerator. It + is templated as T, BV, K which indicate the EBGeometry precision, bounding + volume, and tree degree. + */ + template + class SignedDistanceBVH + { + public: + /*! + @brief Alias for builder node, for encapsulating a "standard" BVH node + */ + using BuilderNode = EBGeometry::BVH::NodeT, BV, K>; + + /*! + @brief Alias for linearized BVH node + */ + using LinearNode = EBGeometry::BVH::LinearBVH, BV, K>; + + /*! + @brief Alias for always-3D vector + */ + using Vec3 = EBGeometry::Vec3T; + + /*! + @brief Full constructor. + @param[in] a_filename File name. Must be a PLY file and will be parser by the + PLY parser. + @param[in] a_flipSign Hook for swapping inside/outside. + */ + SignedDistanceBVH(const std::string a_filename, const bool a_flipSign) + { + + // 1. Read mesh from file. + auto mesh = EBGeometry::Parser::PLY::readIntoDCEL(a_filename); + + // 2. Create a standard BVH hierarchy. This is not a compact ree. + auto root = std::make_shared(mesh->getFaces()); + root->topDownSortAndPartitionPrimitives(EBGeometry::DCEL::defaultBVConstructor, + EBGeometry::DCEL::defaultPartitioner, + EBGeometry::DCEL::defaultStopFunction); + + // 3. Flatten the tree onto a tighter memory representation. + m_rootNode = root->flattenTree(); + } + + /*! + @brief Copy constructor. + @param[in] a_other Other SDF. + */ + SignedDistanceBVH(const SignedDistanceBVH& a_other) + { + this->m_rootNode = a_other.m_rootNode; + this->m_flipSign = a_other.m_flipSign; + } + + /*! + @brief AMReX's implicit function definition. + */ + Real operator()(AMREX_D_DECL(Real x, Real y, Real z)) const noexcept + { + const Real sign = (m_flipSign) ? -1.0 : 1.0; + + return sign * m_rootNode->signedDistance(Vec3(x, y, z)); + }; + + /*! + @brief Also an AMReX implicit function implementation + */ + inline Real + operator()(const RealArray& p) const noexcept + { + return this->operator()(AMREX_D_DECL(p[0], p[1], p[2])); + } + + protected: + /*! + @brief Root node of the linearized BVH hierarchy. + */ + std::shared_ptr m_rootNode; + + /*! + @brief Hook for flipping the sign + */ + bool m_flipSign; + }; + } // namespace EB2 } // namespace amrex int @@ -112,9 +112,9 @@ main(int argc, char* argv[]) { amrex::Initialize(argc, argv); - int n_cell = 128; + int n_cell = 128; int max_grid_size = 32; - int which_geom = 0; + int which_geom = 0; std::string filename; @@ -129,26 +129,32 @@ main(int argc, char* argv[]) RealBox rb; if (which_geom == 0) { // Airfoil case - rb = RealBox({-100, -100, -75}, {400, 100, 125}); + rb = RealBox({-100, -100, -75}, {400, 100, 125}); filename = "../PLY/airfoil.ply"; - } else if (which_geom == 1) { // Sphere case - rb = RealBox({-400, -400, -400}, {400, 400, 400}); + } + else if (which_geom == 1) { // Sphere case + rb = RealBox({-400, -400, -400}, {400, 400, 400}); filename = "../PLY/sphere.ply"; - } else if (which_geom == 2) { // Dodecahedron - rb = RealBox({-2., -2., -2.}, {2., 2., 2.}); + } + else if (which_geom == 2) { // Dodecahedron + rb = RealBox({-2., -2., -2.}, {2., 2., 2.}); filename = "../PLY/dodecahedron.ply"; - } else if (which_geom == 3) { // Horse - rb = RealBox({-0.12, -0.12, -0.12}, {0.12, 0.12, 0.12}); + } + else if (which_geom == 3) { // Horse + rb = RealBox({-0.12, -0.12, -0.12}, {0.12, 0.12, 0.12}); filename = "../PLY/horse.ply"; - } else if (which_geom == 4) { // Car + } + else if (which_geom == 4) { // Car // rb = RealBox({-20,-20,-20}, {20,20,20}); // Doesn't work. - rb = RealBox({-10, -5, -5}, {10, 5, 5}); // Works. + rb = RealBox({-10, -5, -5}, {10, 5, 5}); // Works. filename = "../PLY/porsche.ply"; - } else if (which_geom == 5) { // Orion - rb = RealBox({-10, -5, -10}, {10, 10, 10}); + } + else if (which_geom == 5) { // Orion + rb = RealBox({-10, -5, -10}, {10, 10, 10}); filename = "../PLY/orion.ply"; - } else if (which_geom == 6) { // Armadillo - rb = RealBox({-100, -75, -100}, {100, 125, 100}); + } + else if (which_geom == 6) { // Armadillo + rb = RealBox({-100, -75, -100}, {100, 125, 100}); filename = "../PLY/armadillo.ply"; } @@ -162,9 +168,9 @@ main(int argc, char* argv[]) // EBGeometry precision. constexpr int K = 4; - using T = float; + using T = float; using Vec3 = EBGeometry::Vec3T; - using BV = EBGeometry::BoundingVolumes::AABBT; + using BV = EBGeometry::BoundingVolumes::AABBT; EB2::SignedDistanceBVH sdf(filename, false); diff --git a/Examples/AMReX_Shapes/main.cpp b/Examples/AMReX_Shapes/main.cpp index 1716bb6f..5311c91a 100644 --- a/Examples/AMReX_Shapes/main.cpp +++ b/Examples/AMReX_Shapes/main.cpp @@ -15,55 +15,58 @@ using namespace amrex; -using T = float; -using SDF = EBGeometry::SignedDistanceFunction; +using T = float; +using SDF = EBGeometry::SignedDistanceFunction; using Vec3 = EBGeometry::Vec3T; namespace amrex { -namespace EB2 { - -/*! - @brief This is just an EBGeometry-exposed signed distance field usable with - AMReX. -*/ -class AMReXSDF -{ -public: - /*! -@brief Full constructor. -@param[in] a_filename File name. Must be a PLY file and will be parser by the -PLY parser. -@param[in] a_flipSign Hook for swapping inside/outside. - */ - AMReXSDF(std::shared_ptr& a_sdf) { m_sdf = a_sdf; } - - /*! -@brief Copy constructor. -@param[in] a_other Other SDF. - */ - AMReXSDF(const AMReXSDF& a_other) { this->m_sdf = a_other.m_sdf; } - - /*! -@brief AMReX's implicit function definition. - */ - Real operator()(AMREX_D_DECL(Real x, Real y, Real z)) const noexcept { return m_sdf->signedDistance(Vec3(x, y, z)); }; - - /*! -@brief Also an AMReX implicit function implementation - */ - inline Real - operator()(const RealArray& p) const noexcept - { - return this->operator()(AMREX_D_DECL(p[0], p[1], p[2])); - } - -protected: - /*! -@brief EBGeometry signed distance function. - */ - std::shared_ptr m_sdf; -}; -} // namespace EB2 + namespace EB2 { + + /*! + @brief This is just an EBGeometry-exposed signed distance field usable with + AMReX. + */ + class AMReXSDF + { + public: + /*! + @brief Full constructor. + @param[in] a_filename File name. Must be a PLY file and will be parser by the + PLY parser. + @param[in] a_flipSign Hook for swapping inside/outside. + */ + AMReXSDF(std::shared_ptr& a_sdf) { m_sdf = a_sdf; } + + /*! + @brief Copy constructor. + @param[in] a_other Other SDF. + */ + AMReXSDF(const AMReXSDF& a_other) { this->m_sdf = a_other.m_sdf; } + + /*! + @brief AMReX's implicit function definition. + */ + Real operator()(AMREX_D_DECL(Real x, Real y, Real z)) const noexcept + { + return m_sdf->signedDistance(Vec3(x, y, z)); + }; + + /*! + @brief Also an AMReX implicit function implementation + */ + inline Real + operator()(const RealArray& p) const noexcept + { + return this->operator()(AMREX_D_DECL(p[0], p[1], p[2])); + } + + protected: + /*! + @brief EBGeometry signed distance function. + */ + std::shared_ptr m_sdf; + }; + } // namespace EB2 } // namespace amrex int @@ -71,9 +74,9 @@ main(int argc, char* argv[]) { amrex::Initialize(argc, argv); - int n_cell = 128; + int n_cell = 128; int max_grid_size = 32; - int whichGeom = 0; + int whichGeom = 0; std::string filename; @@ -84,46 +87,55 @@ main(int argc, char* argv[]) pp.query("which_geom", whichGeom); Geometry geom; - RealBox rb; + RealBox rb; std::shared_ptr func; if (whichGeom == 0) { // Sphere. - rb = RealBox({-1, -1, -1}, {1, 1, 1}); + rb = RealBox({-1, -1, -1}, {1, 1, 1}); func = std::make_shared>(Vec3::zero(), T(0.5), false); - } else if (whichGeom == 1) { // Plane. + } + else if (whichGeom == 1) { // Plane. rb = RealBox({-1, -1, -1}, {1, 1, 1}); func = std::make_shared>(Vec3::zero(), Vec3::one(), false); - } else if (whichGeom == 2) { // Infinite cylinder. + } + else if (whichGeom == 2) { // Infinite cylinder. rb = RealBox({-1, -1, -1}, {1, 1, 1}); func = std::make_shared>(Vec3::zero(), T(0.1), 2, false); - } else if (whichGeom == 3) { // Finite cylinder. + } + else if (whichGeom == 3) { // Finite cylinder. rb = RealBox({-2, -2, -2}, {2, 2, 2}); func = std::make_shared>(-Vec3::one(), Vec3::one(), 0.25, false); - } else if (whichGeom == 4) { // Capsule. + } + else if (whichGeom == 4) { // Capsule. rb = RealBox({-2, -2, -2}, {2, 2, 2}); func = std::make_shared>(-Vec3::one(), Vec3::one(), 0.25, false); - } else if (whichGeom == 5) { // Box. + } + else if (whichGeom == 5) { // Box. rb = RealBox({-2, -2, -2}, {2, 2, 2}); func = std::make_shared>(-Vec3::one(), Vec3::one(), false); - } else if (whichGeom == 6) { // Rounded box. + } + else if (whichGeom == 6) { // Rounded box. rb = RealBox({-2, -2, -2}, {2, 2, 2}); auto box = std::make_shared>(-Vec3::one(), Vec3::one(), false); - func = std::make_shared>(box, 0.25); - } else if (whichGeom == 7) { // Torus. + func = std::make_shared>(box, 0.25); + } + else if (whichGeom == 7) { // Torus. rb = RealBox({-2, -2, -2}, {2, 2, 2}); func = std::make_shared>(Vec3::zero(), 1.0, 0.25, false); - } else if (whichGeom == 8) { // Infinite cone. + } + else if (whichGeom == 8) { // Infinite cone. rb = RealBox({-2, -2, -2}, {2, 2, 2}); func = std::make_shared>(Vec3(0.0, 0.0, 1.0), 30.0, false); - } else if (whichGeom == 9) { // Finite cone. + } + else if (whichGeom == 9) { // Finite cone. rb = RealBox({-2, -2, -2}, {2, 2, 2}); func = std::make_shared>(Vec3(0.0, 0.0, 1.0), 2.0, 30, false); @@ -132,7 +144,7 @@ main(int argc, char* argv[]) rb = RealBox({-1, -1, -1}, {1, 1, 1}); auto sphere = std::make_shared>(Vec3::zero(), T(0.5), false); - func = std::make_shared>(sphere, 0.1); + func = std::make_shared>(sphere, 0.1); } Array is_periodic{false, false, false}; diff --git a/Examples/Chombo3_DCEL/main.cpp b/Examples/Chombo3_DCEL/main.cpp index bb6c3582..1067c1bf 100644 --- a/Examples/Chombo3_DCEL/main.cpp +++ b/Examples/Chombo3_DCEL/main.cpp @@ -13,10 +13,10 @@ // Our includes #include "EBGeometry.hpp" -using T = float; -using SDF = EBGeometry::SignedDistanceFunction; +using T = float; +using SDF = EBGeometry::SignedDistanceFunction; using Vec3 = EBGeometry::Vec3T; -using BV = EBGeometry::BoundingVolumes::AABBT; +using BV = EBGeometry::BoundingVolumes::AABBT; // Binding for exposing EBGeometry's signed distance functions to Chombo template @@ -42,9 +42,9 @@ class ChomboSDF : public BaseIF // 2. Create standard BVH hierarchy. This is not a compact tree. auto root = std::make_shared(mesh->getFaces()); - root->topDownSortAndPartitionPrimitives( - EBGeometry::DCEL::defaultBVConstructor, EBGeometry::DCEL::defaultPartitioner, - EBGeometry::DCEL::defaultStopFunction); + root->topDownSortAndPartitionPrimitives(EBGeometry::DCEL::defaultBVConstructor, + EBGeometry::DCEL::defaultPartitioner, + EBGeometry::DCEL::defaultStopFunction); // 3. Flatten the tree onto a tighter memory representation. m_rootNode = root->flattenTree(); @@ -85,18 +85,18 @@ main(int argc, char* argv[]) // Set up domain. // Parse input file - char* inFile = argv[1]; + char* inFile = argv[1]; ParmParse pp(argc - 2, argv + 2, NULL, inFile); - int nCells = 128; + int nCells = 128; int whichGeom = 0; - int gridSize = 16; + int gridSize = 16; pp.query("which_geom", whichGeom); pp.query("n_cells", nCells); pp.query("grid_size", gridSize); - RealVect loCorner; - RealVect hiCorner; + RealVect loCorner; + RealVect hiCorner; std::string filename; if (whichGeom == 0) { // Airfoil @@ -104,32 +104,38 @@ main(int argc, char* argv[]) hiCorner = 250 * RealVect::Unit; filename = "../PLY/airfoil.ply"; - } else if (whichGeom == 1) { // Sphere + } + else if (whichGeom == 1) { // Sphere loCorner = -400 * RealVect::Unit; hiCorner = 400 * RealVect::Unit; filename = "../PLY/sphere.ply"; - } else if (whichGeom == 2) { // Dodecahedron + } + else if (whichGeom == 2) { // Dodecahedron loCorner = -2 * RealVect::Unit; hiCorner = 2 * RealVect::Unit; filename = "../PLY/dodecahedron.ply"; - } else if (whichGeom == 3) { // Horse + } + else if (whichGeom == 3) { // Horse loCorner = -0.12 * RealVect::Unit; hiCorner = 0.12 * RealVect::Unit; filename = "../PLY/horse.ply"; - } else if (whichGeom == 4) { // Porsche + } + else if (whichGeom == 4) { // Porsche loCorner = -10 * RealVect::Unit; hiCorner = 10 * RealVect::Unit; filename = "../PLY/porsche.ply"; - } else if (whichGeom == 5) { // Orion + } + else if (whichGeom == 5) { // Orion loCorner = -10 * RealVect::Unit; hiCorner = 10 * RealVect::Unit; filename = "../PLY/orion.ply"; - } else if (whichGeom == 6) { // Armadillo + } + else if (whichGeom == 6) { // Armadillo loCorner = -125 * RealVect::Unit; hiCorner = 125 * RealVect::Unit; @@ -137,15 +143,15 @@ main(int argc, char* argv[]) } // - constexpr int K = 4; - auto impFunc = (BaseIF*)(new ChomboSDF(filename)); + constexpr int K = 4; + auto impFunc = (BaseIF*)(new ChomboSDF(filename)); // Set up the Chombo EB geometry. ProblemDomain domain(IntVect::Zero, (nCells - 1) * IntVect::Unit); - const Real dx = (hiCorner[0] - loCorner[0]) / nCells; + const Real dx = (hiCorner[0] - loCorner[0]) / nCells; ; - GeometryShop workshop(*impFunc, -1, dx * RealVect::Zero); + GeometryShop workshop(*impFunc, -1, dx * RealVect::Zero); EBIndexSpace* ebisPtr = Chombo_EBIS::instance(); ebisPtr->define(domain, loCorner, dx, workshop, gridSize, -1); @@ -171,7 +177,7 @@ main(int argc, char* argv[]) for (BoxIterator bit(region); bit.ok(); ++bit) { const IntVect iv = bit(); - const RealVect pos = loCorner + (iv + 0.5 * RealVect::Unit) * dx; + const RealVect pos = loCorner + (iv + 0.5 * RealVect::Unit) * dx; fab.getFArrayBox()(iv, 0) = impFunc->value(pos); } } diff --git a/Examples/Chombo3_Shapes/main.cpp b/Examples/Chombo3_Shapes/main.cpp index 38643fd3..ae8f1f33 100644 --- a/Examples/Chombo3_Shapes/main.cpp +++ b/Examples/Chombo3_Shapes/main.cpp @@ -13,8 +13,8 @@ // Our includes #include "EBGeometry.hpp" -using T = float; -using SDF = EBGeometry::SignedDistanceFunction; +using T = float; +using SDF = EBGeometry::SignedDistanceFunction; using Vec3 = EBGeometry::Vec3T; // Binding for exposing EBGeometry's signed distance functions to Chombo @@ -61,12 +61,12 @@ main(int argc, char* argv[]) // Set up domain. // Parse input file - char* inFile = argv[1]; + char* inFile = argv[1]; ParmParse pp(argc - 2, argv + 2, NULL, inFile); - int nCells = 128; + int nCells = 128; int whichGeom = 0; - int gridSize = 16; + int gridSize = 16; pp.query("which_geom", whichGeom); pp.query("n_cells", nCells); pp.query("grid_size", gridSize); @@ -80,48 +80,57 @@ main(int argc, char* argv[]) hiCorner = RealVect::Unit; sdf = std::make_shared>(Vec3::zero(), T(0.5), false); - } else if (whichGeom == 1) { // Plane. + } + else if (whichGeom == 1) { // Plane. loCorner = -RealVect::Unit; hiCorner = RealVect::Unit; sdf = std::make_shared>(Vec3::zero(), Vec3::one(), false); - } else if (whichGeom == 2) { // Infinite cylinder. + } + else if (whichGeom == 2) { // Infinite cylinder. loCorner = -RealVect::Unit; hiCorner = RealVect::Unit; sdf = std::make_shared>(Vec3::zero(), T(0.1), 2, false); - } else if (whichGeom == 3) { // Finite cylinder. + } + else if (whichGeom == 3) { // Finite cylinder. loCorner = -2 * RealVect::Unit; hiCorner = 2 * RealVect::Unit; sdf = std::make_shared>(-Vec3::one(), Vec3::one(), 0.25, false); - } else if (whichGeom == 4) { // Capsule. + } + else if (whichGeom == 4) { // Capsule. loCorner = -2 * RealVect::Unit; hiCorner = 2 * RealVect::Unit; sdf = std::make_shared>(-Vec3::one(), Vec3::one(), 0.25, false); - } else if (whichGeom == 5) { // Box. + } + else if (whichGeom == 5) { // Box. loCorner = -2 * RealVect::Unit; hiCorner = 2 * RealVect::Unit; sdf = std::make_shared>(-Vec3::one(), Vec3::one(), false); - } else if (whichGeom == 6) { // Rounded box. + } + else if (whichGeom == 6) { // Rounded box. loCorner = -2 * RealVect::Unit; hiCorner = 2 * RealVect::Unit; auto box = std::make_shared>(-Vec3::one(), Vec3::one(), false); - sdf = std::make_shared>(box, 0.25); - } else if (whichGeom == 7) { // Torus. + sdf = std::make_shared>(box, 0.25); + } + else if (whichGeom == 7) { // Torus. loCorner = -2 * RealVect::Unit; hiCorner = 2 * RealVect::Unit; sdf = std::make_shared>(Vec3::zero(), 1.0, 0.25, false); - } else if (whichGeom == 8) { // Infinite cone. + } + else if (whichGeom == 8) { // Infinite cone. loCorner = -2 * RealVect::Unit; hiCorner = 2 * RealVect::Unit; sdf = std::make_shared>(Vec3(0.0, 0.0, 1.0), 30.0, false); - } else if (whichGeom == 9) { // Finite cone. + } + else if (whichGeom == 9) { // Finite cone. loCorner = -2 * RealVect::Unit; hiCorner = 2 * RealVect::Unit; @@ -132,15 +141,15 @@ main(int argc, char* argv[]) hiCorner = RealVect::Unit; auto sphere = std::make_shared>(Vec3::zero(), T(0.5), false); - sdf = std::make_shared>(sphere, 0.1); + sdf = std::make_shared>(sphere, 0.1); } // Set up the Chombo EB geometry. ProblemDomain domain(IntVect::Zero, (nCells - 1) * IntVect::Unit); - const Real dx = (hiCorner[0] - loCorner[0]) / nCells; + const Real dx = (hiCorner[0] - loCorner[0]) / nCells; ; - auto impFunc = (BaseIF*)(new ChomboSDF(sdf)); - GeometryShop workshop(*impFunc, -1, dx * RealVect::Zero); + auto impFunc = (BaseIF*)(new ChomboSDF(sdf)); + GeometryShop workshop(*impFunc, -1, dx * RealVect::Zero); EBIndexSpace* ebisPtr = Chombo_EBIS::instance(); ebisPtr->define(domain, loCorner, dx, workshop, gridSize, -1); @@ -166,7 +175,7 @@ main(int argc, char* argv[]) for (BoxIterator bit(region); bit.ok(); ++bit) { const IntVect iv = bit(); - const RealVect pos = loCorner + (iv + 0.5 * RealVect::Unit) * dx; + const RealVect pos = loCorner + (iv + 0.5 * RealVect::Unit) * dx; fab.getFArrayBox()(iv, 0) = impFunc->value(pos); } } diff --git a/Examples/EBGeometry_DCEL/main.cpp b/Examples/EBGeometry_DCEL/main.cpp index 246d0124..862c16d4 100644 --- a/Examples/EBGeometry_DCEL/main.cpp +++ b/Examples/EBGeometry_DCEL/main.cpp @@ -20,7 +20,7 @@ int main(int argc, char* argv[]) { - std::string current_exec_name = argv[0]; // Name of the current exec program + std::string current_exec_name = argv[0]; // Name of the current exec program std::vector all_args; std::string file; @@ -28,7 +28,8 @@ main(int argc, char* argv[]) // Read the input file. if (argc == 2) { file = "../PLY/" + std::string(argv[1]); - } else { + } + else { std::cerr << "Missing file name. Use ./a.out 'filename' where 'filename' " "is one of the files in ../PLY\n"; } @@ -37,7 +38,7 @@ main(int argc, char* argv[]) using T = float; // Aliases for cutting down on typing. - using BV = BoundingVolumes::AABBT; + using BV = BoundingVolumes::AABBT; using Vec3 = Vec3T; // Parse the mesh from file. One can call the signed distance function @@ -50,9 +51,9 @@ main(int argc, char* argv[]) // bounding volume hierarchy bounds the facets in a binary tree. std::cout << "Partitioning BVH\n"; auto bvhSDF = std::make_shared, BV, K>>(directSDF->getFaces()); - bvhSDF->topDownSortAndPartitionPrimitives( - EBGeometry::DCEL::defaultBVConstructor, EBGeometry::DCEL::defaultPartitioner, - EBGeometry::DCEL::defaultStopFunction); + bvhSDF->topDownSortAndPartitionPrimitives(EBGeometry::DCEL::defaultBVConstructor, + EBGeometry::DCEL::defaultPartitioner, + EBGeometry::DCEL::defaultStopFunction); // Create the linear representation of the conventional BVH SDF above. std::cout << "Flattening BVH tree\n"; @@ -61,27 +62,27 @@ main(int argc, char* argv[]) // Compute signed distance for this position and time all SDF representations. const Vec3 point = Vec3::one(); - const auto t1 = std::chrono::high_resolution_clock::now(); - const T directDist = directSDF->signedDistance(point); - const auto t2 = std::chrono::high_resolution_clock::now(); + const auto t1 = std::chrono::high_resolution_clock::now(); + const T directDist = directSDF->signedDistance(point); + const auto t2 = std::chrono::high_resolution_clock::now(); - const auto t3 = std::chrono::high_resolution_clock::now(); - const T bvhDist = bvhSDF->signedDistance(point); - const auto t4 = std::chrono::high_resolution_clock::now(); + const auto t3 = std::chrono::high_resolution_clock::now(); + const T bvhDist = bvhSDF->signedDistance(point); + const auto t4 = std::chrono::high_resolution_clock::now(); - const auto t5 = std::chrono::high_resolution_clock::now(); - const T linDist = linSDF->signedDistance(point); - const auto t6 = std::chrono::high_resolution_clock::now(); + const auto t5 = std::chrono::high_resolution_clock::now(); + const T linDist = linSDF->signedDistance(point); + const auto t6 = std::chrono::high_resolution_clock::now(); // Kill all the SDF representations. directSDF = nullptr; - bvhSDF = nullptr; - linSDF = nullptr; + bvhSDF = nullptr; + linSDF = nullptr; // Get the timings. const std::chrono::duration directTime = t2 - t1; - const std::chrono::duration bvhTime = t4 - t3; - const std::chrono::duration linTime = t6 - t5; + const std::chrono::duration bvhTime = t4 - t3; + const std::chrono::duration linTime = t6 - t5; std::cout << "Distance and time using direct query = " << directDist << ", which took " << directTime.count() << " us\n"; diff --git a/Examples/EBGeometry_Union/main.cpp b/Examples/EBGeometry_Union/main.cpp index 8e689ccd..17c790d3 100644 --- a/Examples/EBGeometry_Union/main.cpp +++ b/Examples/EBGeometry_Union/main.cpp @@ -14,9 +14,9 @@ using T = double; // Aliases for cutting down on typing. -using AABB = EBGeometry::BoundingVolumes::AABBT; -using Vec3 = EBGeometry::Vec3T; -using SDF = EBGeometry::SignedDistanceFunction; +using AABB = EBGeometry::BoundingVolumes::AABBT; +using Vec3 = EBGeometry::Vec3T; +using SDF = EBGeometry::SignedDistanceFunction; using Sphere = EBGeometry::SphereSDF; using namespace std::chrono_literals; @@ -32,8 +32,8 @@ main() // the spheres is 2*radius std::vector> spheres; - constexpr T radius = 1.0; - constexpr int N = 100; + constexpr T radius = 1.0; + constexpr int N = 100; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { @@ -62,7 +62,7 @@ main() const Sphere& sph = static_cast(*a_prim); const Vec3& c = sph.getCenter(); - const T& r = sph.getRadius(); + const T& r = sph.getRadius(); const Vec3 lo = c - r * Vec3::one(); const Vec3 hi = c + r * Vec3::one(); @@ -77,14 +77,14 @@ main() const Vec3 point = Vec3::zero(); std::cout << "Computing distance with slow union\n"; - const auto t1 = std::chrono::high_resolution_clock::now(); - const T slowDist = slowUnion.signedDistance(point); - const auto t2 = std::chrono::high_resolution_clock::now(); + const auto t1 = std::chrono::high_resolution_clock::now(); + const T slowDist = slowUnion.signedDistance(point); + const auto t2 = std::chrono::high_resolution_clock::now(); std::cout << "Computing distance with fast union\n"; - const auto t3 = std::chrono::high_resolution_clock::now(); - const T fastDist = fastUnion.signedDistance(point); - const auto t4 = std::chrono::high_resolution_clock::now(); + const auto t3 = std::chrono::high_resolution_clock::now(); + const T fastDist = fastUnion.signedDistance(point); + const auto t4 = std::chrono::high_resolution_clock::now(); const std::chrono::duration slowTime = t2 - t1; const std::chrono::duration fastTime = t4 - t3; diff --git a/README.md b/README.md index de2ff886..dfd7991b 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Contributing ``` git checkout main git pull - git checkout -b my_feature + git checkout -b my_branch ``` 2. Develop the feature. @@ -96,7 +96,7 @@ Contributing 5. Push the changes to GitHub ``` - git push --set-upstream my_feature + git push --set-upstream origin my_branch ``` 6. Create a pull request and make sure the GitHub continuous integration tests pass. diff --git a/Source/EBGeometry_AnalyticDistanceFunctions.hpp b/Source/EBGeometry_AnalyticDistanceFunctions.hpp index f3fd7d4b..f2cdc4f5 100644 --- a/Source/EBGeometry_AnalyticDistanceFunctions.hpp +++ b/Source/EBGeometry_AnalyticDistanceFunctions.hpp @@ -58,7 +58,7 @@ class RoundedSDF : public SignedDistanceFunction */ RoundedSDF(const std::shared_ptr> a_sdf, const T a_curv) { - m_sdf = a_sdf; + m_sdf = a_sdf; m_curv = a_curv; } @@ -107,7 +107,7 @@ class AnnularSDF : public SignedDistanceFunction */ AnnularSDF(const std::shared_ptr> a_sdf, const T a_curv) { - m_sdf = a_sdf; + m_sdf = a_sdf; m_curv = a_curv; } @@ -156,7 +156,7 @@ class ScaledSDF : public SignedDistanceFunction */ ScaledSDF(const std::shared_ptr> a_sdf, const T a_scale) { - m_sdf = a_sdf; + m_sdf = a_sdf; m_scale = a_scale; } @@ -204,8 +204,8 @@ class PlaneSDF : public SignedDistanceFunction */ PlaneSDF(const Vec3T& a_point, const Vec3T& a_normal, const bool a_flipInside) { - m_point = a_point; - m_normal = a_normal; + m_point = a_point; + m_normal = a_normal; m_flipInside = a_flipInside; m_normal /= m_normal.length(); @@ -260,8 +260,8 @@ class SphereSDF : public SignedDistanceFunction */ SphereSDF(const Vec3T& a_center, const T& a_radius, const bool a_flipInside) { - this->m_center = a_center; - this->m_radius = a_radius; + this->m_center = a_center; + this->m_radius = a_radius; this->m_flipInside = a_flipInside; } @@ -270,9 +270,9 @@ class SphereSDF : public SignedDistanceFunction */ SphereSDF(const SphereSDF& a_other) { - this->m_center = a_other.m_center; - this->m_radius = a_other.m_radius; - this->m_flipInside = a_other.m_flipInside; + this->m_center = a_other.m_center; + this->m_radius = a_other.m_radius; + this->m_flipInside = a_other.m_flipInside; this->m_transformOps = a_other.m_transformOps; } @@ -366,8 +366,8 @@ class BoxSDF : public SignedDistanceFunction */ BoxSDF(const Vec3T& a_loCorner, const Vec3T& a_hiCorner, const bool a_flipInside) { - this->m_loCorner = a_loCorner; - this->m_hiCorner = a_hiCorner; + this->m_loCorner = a_loCorner; + this->m_hiCorner = a_hiCorner; this->m_flipInside = a_flipInside; } @@ -427,16 +427,12 @@ class BoxSDF : public SignedDistanceFunction // between xLo and xHi. In this case delta[dir] will be the signed distance // to the closest box face in the dir-direction. Otherwise, if a_point[dir] // is outside the corner we have delta[dir] > 0. - const Vec3T delta( - std::max( - m_loCorner[0] - a_point[0], - a_point[0] - m_hiCorner[0]), // < 0 if point falls between xLo and xHi. - std::max( - m_loCorner[1] - a_point[1], - a_point[1] - m_hiCorner[1]), // < 0 if point falls between yLo and yHi. - std::max( - m_loCorner[2] - a_point[2], - a_point[2] - m_hiCorner[2])); // < 0 if point falls between zLo and zHi. + const Vec3T delta(std::max(m_loCorner[0] - a_point[0], + a_point[0] - m_hiCorner[0]), // < 0 if point falls between xLo and xHi. + std::max(m_loCorner[1] - a_point[1], + a_point[1] - m_hiCorner[1]), // < 0 if point falls between yLo and yHi. + std::max(m_loCorner[2] - a_point[2], + a_point[2] - m_hiCorner[2])); // < 0 if point falls between zLo and zHi. // Note: max is max(Vec3T, Vec3T) and not std::max. It returns a // vector with coordinate-wise largest components. Note that the first part @@ -489,10 +485,10 @@ class TorusSDF : public SignedDistanceFunction */ TorusSDF(const Vec3T& a_center, const T& a_majorRadius, const T& a_minorRadius, const bool a_flipInside) { - this->m_center = a_center; + this->m_center = a_center; this->m_majorRadius = a_majorRadius; this->m_minorRadius = a_minorRadius; - this->m_flipInside = a_flipInside; + this->m_flipInside = a_flipInside; } /*! @@ -566,9 +562,9 @@ class TorusSDF : public SignedDistanceFunction virtual T signedDistance(const Vec3T& a_point) const noexcept override { - const Vec3T p = a_point - m_center; - const T rho = sqrt(p[0] * p[0] + p[1] * p[1]) - m_majorRadius; - const T d = sqrt(rho * rho + p[2] * p[2]) - m_minorRadius; + const Vec3T p = a_point - m_center; + const T rho = sqrt(p[0] * p[0] + p[1] * p[1]) - m_majorRadius; + const T d = sqrt(rho * rho + p[2] * p[2]) - m_minorRadius; const T sign = m_flipInside ? -1.0 : 1.0; @@ -618,15 +614,15 @@ class CylinderSDF : public SignedDistanceFunction */ CylinderSDF(const Vec3T& a_center1, const Vec3T& a_center2, const T& a_radius, const bool a_flipInside) { - this->m_center1 = a_center1; - this->m_center2 = a_center2; - this->m_radius = a_radius; + this->m_center1 = a_center1; + this->m_center2 = a_center2; + this->m_radius = a_radius; this->m_flipInside = a_flipInside; // Some derived quantities that are needed for SDF computations. m_center = (m_center2 + m_center1) * 0.5; m_length = (m_center2 - m_center1).length(); - m_axis = (m_center2 - m_center1) / m_length; + m_axis = (m_center2 - m_center1) / m_length; } /*! @@ -675,7 +671,7 @@ class CylinderSDF : public SignedDistanceFunction if (m_length > 0.0 && m_radius > 0.0) { const Vec3T point = a_point - m_center; - const T para = dot(point, m_axis); + const T para = dot(point, m_axis); const Vec3T ortho = point - para * m_axis; // Distance from cylinder axis. const T w = ortho.length() - m_radius; // Distance from cylinder wall. < 0 @@ -687,12 +683,15 @@ class CylinderSDF : public SignedDistanceFunction if (w <= zero && h <= zero) { // Inside cylinder d = (std::abs(w) < std::abs(h)) ? w : h; - } else if (w <= zero && h > zero) { // Above one of the endcaps. + } + else if (w <= zero && h > zero) { // Above one of the endcaps. d = h; - } else if (w > zero && h < zero) { // Outside radius but between the - // endcaps. + } + else if (w > zero && h < zero) { // Outside radius but between the + // endcaps. d = w; - } else { + } + else { d = sqrt(w * w + h * h); } } @@ -760,9 +759,9 @@ class InfiniteCylinderSDF : public SignedDistanceFunction */ InfiniteCylinderSDF(const Vec3T& a_center, const T& a_radius, const size_t a_axis, const bool a_flipInside) { - m_center = a_center; - m_radius = a_radius; - m_axis = a_axis; + m_center = a_center; + m_radius = a_radius; + m_axis = a_axis; m_flipInside = a_flipInside; } @@ -774,9 +773,9 @@ class InfiniteCylinderSDF : public SignedDistanceFunction signedDistance(const Vec3T& a_point) const noexcept override { Vec3T delta = a_point - m_center; - delta[m_axis] = 0.0; + delta[m_axis] = 0.0; - const T d = delta.length() - m_radius; + const T d = delta.length() - m_radius; const T sign = m_flipInside ? -1.0 : 1.0; return sign * d; @@ -827,10 +826,10 @@ class CapsuleSDF : public SignedDistanceFunction CapsuleSDF(const Vec3T& a_tip1, const Vec3T a_tip2, const T& a_radius, const bool a_flipInside) { const Vec3T axis = (a_tip2 - a_tip1) / length(a_tip2 - a_tip1); - m_center1 = a_tip1 + a_radius * axis; - m_center2 = a_tip2 - a_radius * axis; - m_radius = a_radius; - m_flipInside = a_flipInside; + m_center1 = a_tip1 + a_radius * axis; + m_center2 = a_tip2 - a_radius * axis; + m_radius = a_radius; + m_flipInside = a_flipInside; } /*! @@ -843,8 +842,8 @@ class CapsuleSDF : public SignedDistanceFunction const Vec3T v1 = a_point - m_center1; const Vec3T v2 = m_center2 - m_center1; - const T h = clamp(dot(v1, v2) / dot(v2, v2), T(0.0), T(1.0)); - const T d = length(v1 - h * v2) - m_radius; + const T h = clamp(dot(v1, v2) / dot(v2, v2), T(0.0), T(1.0)); + const T d = length(v1 - h * v2) - m_radius; const T sign = m_flipInside ? -1.0 : 1.0; return sign * d; @@ -894,9 +893,9 @@ class InfiniteConeSDF : public SignedDistanceFunction { constexpr T pi = 3.14159265358979323846; - m_tip = a_tip; - m_c.x = std::sin(0.5 * a_angle * pi / 180.0); - m_c.y = std::cos(0.5 * a_angle * pi / 180.0); + m_tip = a_tip; + m_c.x = std::sin(0.5 * a_angle * pi / 180.0); + m_c.y = std::cos(0.5 * a_angle * pi / 180.0); m_flipInside = a_flipInside; } @@ -963,10 +962,10 @@ class ConeSDF : public SignedDistanceFunction { constexpr T pi = 3.14159265358979323846; - m_tip = a_tip; - m_height = a_height; - m_c.x = std::sin(0.5 * a_angle * pi / 180.0); - m_c.y = std::cos(0.5 * a_angle * pi / 180.0); + m_tip = a_tip; + m_height = a_height; + m_c.x = std::sin(0.5 * a_angle * pi / 180.0); + m_c.y = std::cos(0.5 * a_angle * pi / 180.0); m_flipInside = a_flipInside; } @@ -983,11 +982,11 @@ class ConeSDF : public SignedDistanceFunction signedDistance(const Vec3T& a_point) const noexcept override { const Vec3T delta = a_point - m_tip; - const T dr = sqrt(delta[0] * delta[0] + delta[1] * delta[1]); - const T dz = delta[2]; + const T dr = sqrt(delta[0] * delta[0] + delta[1] * delta[1]); + const T dz = delta[2]; constexpr T zero = T(0.0); - constexpr T one = T(1.0); + constexpr T one = T(1.0); const Vec2T q = m_height * Vec2T(m_c.x / m_c.y, -1.0); const Vec2T w = Vec2T(dr, dz); @@ -996,9 +995,9 @@ class ConeSDF : public SignedDistanceFunction auto sign = [](const T& x) { return (x > zero) - (x < zero); }; - const T k = sign(q.y); - const T d = std::min(dot(a, a), dot(b, b)); - const T s = std::max(k * (w.x * q.y - w.y * q.x), k * (w.y - q.y)); + const T k = sign(q.y); + const T d = std::min(dot(a, a), dot(b, b)); + const T s = std::max(k * (w.x * q.y - w.y * q.x), k * (w.y - q.y)); const T flip = m_flipInside ? -one : one; return flip * sqrt(d) * sign(s); diff --git a/Source/EBGeometry_BVH.hpp b/Source/EBGeometry_BVH.hpp index cc58d3b8..c9fad17e 100644 --- a/Source/EBGeometry_BVH.hpp +++ b/Source/EBGeometry_BVH.hpp @@ -27,553 +27,551 @@ */ namespace BVH { -/*! - @brief Forward declare the BVH node since it is needed for the polymorphic - lambdas. - @details T is the precision used in the BVH computations, P is the enclosing - primitive and BV is the bounding volume used in the BVH. K is the tree degree. -*/ -template -class NodeT; - -/*! - @brief Forward declare linear node class. - @details T is the precision used in the BVH computations, P is the enclosing - primitive and BV is the bounding volume used in the BVH. K is the tree degree. -*/ -template -class LinearNodeT; - -/*! - @brief Forward declare linear BVH class. - @details T is the precision used in the BVH computations, P is the enclosing - primitive and BV is the bounding volume used in the BVH. K is the tree degree. -*/ -template -class LinearBVH; - -/*! - @brief Alias to cut down on typing. - @details P is the primitive bounded by the BVH. -*/ -template -using PrimitiveListT = std::vector>; - -/*! - @brief Stop function for deciding when a BVH node can't be divided into - sub-volumes. - @details T is the precision used in the BVH computations, P is the enclosing - primitive and BV is the bounding volume used in the BVH. K is the tree degree. - @param[in] a_node BVH node - @return True if the node can't be divided into subvolumes and false otherwise. -*/ -template -using StopFunctionT = std::function& a_node)>; - -/*! - @brief Polymorphic partitioner for splitting a list of primitives into K new - lists of primitives - @details P is the primitive type bound in the BVH and K is the BVH degree. - @param[in] a_primitives List of primitives to be subdivided into sub-bounding - volumes. - @return Returns a list (std::array) of new primitives which make up the new - bounding volumes. -*/ -template -using PartitionerT = std::function, K>(const PrimitiveListT

& a_primitives)>; - -/*! - @brief Constructor method for creating bounding volumes from a list of - primitives - @details P is the primitive type bound in the BVH and BV is the bounding - volume type. - @param[in] a_primitives List of primitives. - @return Returns a new bounding volumes which is guaranteed to enclose all the - input primitives. -*/ -template -using BVConstructorT = std::function& a_primitive)>; - -/*! - @brief Typename for identifying algorithms various algorithms during tree - traversel. - @details Stack => Use stack/priority queue (ordered traversal). - Ordered => Use recursive ordered traversal. - Unordered => Use recursive unordered traversal. -*/ -enum class Prune -{ - Stack, - Ordered, - Unordered, -}; - -/*! - @brief Class which encapsulates a node in a bounding volume hierarchy. - @details T is the precision, P is the primitive type you want to enclose, BV - is the bounding volume type used at the nodes. The parameter K (which must be - > 1) is the tree degree. K=2 is a binary tree, K=3 is a tertiary tree and so - on. - @note Template constraints are as following: - - P MUST supply function signedDistance(...) . - BV MUST supply a function getDistance - - Had this been C++20, we would have use concepts to enforce this. -*/ -template -class NodeT : public SignedDistanceFunction -{ -public: - /*! - @brief Alias for cutting down on typing. This is a - std::vector >. - */ - using PrimitiveList = PrimitiveListT

; - - /*! - @brief Alias for cutting down on typing. - */ - using Vec3 = Vec3T; - - /*! - @brief Alias for cutting down on typing. - */ - using Node = NodeT; - - /*! - @brief Alias for cutting down on typing. - */ - using NodePtr = std::shared_ptr; - - /*! - @brief Alias for cutting down on typing. - */ - using StopFunction = StopFunctionT; - - /*! - @brief Alias for cutting down on typing - */ - using Partitioner = PartitionerT; - - /*! - @brief Alias for cutting down on typing. - */ - using BVConstructor = BVConstructorT; - - /*! - @brief Default constructor which sets a regular node. - */ - NodeT(); - - /*! - @brief Construct node from a set of primitives. - @details This node becomes a leaf node which contains the input primitives. - @param[in] a_primitives Input primitives. - */ - NodeT(const std::vector>& a_primitives); - - /*! - @brief Construct node from a set of primitives. - @details This node becomes a leaf node which contains the input primitives. - @param[in] a_primitives Input primitives. - */ - NodeT(const std::vector>& a_primitives); - - /*! - @brief Destructor (does nothing) - */ - virtual ~NodeT(); - - /*! - @brief Function for using top-down construction of the bounding volume - hierarchy. - @details The rules for terminating the hierarchy construction, how to - partition sets of primitives, and how to enclose them by bounding volumes - are given in the input arguments (a_stopFunc, a_partFunc, a_bvFunc) - @param[in] a_bvConstructor Polymorphic function which builds a bounding - volume from a set of primitives. - @param[in] a_partitioner Partitioning function. This is a polymorphic - function which divides a set of primitives into two lists. - @param[in] a_stopCrit Termination function which tells us when to stop - the recursion. - */ - inline void - topDownSortAndPartitionPrimitives( - const BVConstructor& a_bvConstructor, const Partitioner& a_partitioner, const StopFunction& a_stopCrit) noexcept; - - /*! - @brief Get node type - */ - inline bool - isLeaf() const noexcept; - - /*! - @brief Get the primitives stored in this node. - @return m_primitives. - */ - inline const PrimitiveList& - getPrimitives() const noexcept; - - /*! - @brief Get bounding volume - @return m_bv - */ - inline const BV& - getBoundingVolume() const noexcept; - - /*! - @brief Return this node's children. - @return m_children. - */ - inline const std::array>, K>& - getChildren() const noexcept; - - /*! - @brief Function which computes the signed distance - @param[in] a_point 3D point in space - @return Signed distance to the input point. - */ - inline T - signedDistance(const Vec3T& a_point) const noexcept override; - - /*! - @brief Function which computes the signed distance. This version allows the - user to manually select a traversal algorithm. - @param[in] a_point 3D point in space - @param[in] a_pruning Pruning algorithm - @return Signed distance to the input point. - */ - inline T - signedDistance(const Vec3T& a_point, const Prune a_pruning) const noexcept; - - /*! - @brief Flatten everything beneath this node into a depth-first sorted BVH - hierarchy. - @details This will compute the flattening of the standard BVH tree and - return a pointer to the linear corresponding to the current node. - */ - inline std::shared_ptr> - flattenTree() const noexcept; - -protected: - /*! - @brief Bounding volume object. - */ - BV m_boundingVolume; - - /*! - @brief Primitives list. This will be empty for regular nodes - */ - std::vector> m_primitives; - - /*! - @brief Children nodes - */ - std::array>, K> m_children; - /*! - @brief Insert nodes with primitives. - @param[in] a_primitives Primitives for children. + @brief Forward declare the BVH node since it is needed for the polymorphic + lambdas. + @details T is the precision used in the BVH computations, P is the enclosing + primitive and BV is the bounding volume used in the BVH. K is the tree degree. */ - inline void - insertChildren(const std::array& a_primitives) noexcept; + template + class NodeT; /*! - @brief Set primitives in this node - @param[in] a_primitives Primitives + @brief Forward declare linear node class. + @details T is the precision used in the BVH computations, P is the enclosing + primitive and BV is the bounding volume used in the BVH. K is the tree degree. */ - inline void - setPrimitives(const PrimitiveList& a_primitives) noexcept; + template + class LinearNodeT; /*! - @brief Get the distance from a 3D point to the bounding volume - @param[in] a_point 3D point - @return Returns distance to bounding volume. A zero distance implies that - the input point is inside the bounding volume. + @brief Forward declare linear BVH class. + @details T is the precision used in the BVH computations, P is the enclosing + primitive and BV is the bounding volume used in the BVH. K is the tree degree. */ - inline T - getDistanceToBoundingVolume(const Vec3& a_point) const noexcept; + template + class LinearBVH; /*! - @brief Compute the shortest distance to the primitives in this node. - @param[in] a_point 3D point - @return Returns the signed distance to the primitives. + @brief Alias to cut down on typing. + @details P is the primitive bounded by the BVH. */ - inline T - getDistanceToPrimitives(const Vec3& a_point) const noexcept; + template + using PrimitiveListT = std::vector>; /*! - @brief Get the list of primitives in this node. - @return Primitives list + @brief Stop function for deciding when a BVH node can't be divided into + sub-volumes. + @details T is the precision used in the BVH computations, P is the enclosing + primitive and BV is the bounding volume used in the BVH. K is the tree degree. + @param[in] a_node BVH node + @return True if the node can't be divided into subvolumes and false otherwise. */ - inline PrimitiveList& - getPrimitives() noexcept; + template + using StopFunctionT = std::function& a_node)>; /*! - @brief Iterative ordered pruning along the BVH tree. - @param[in,out] a_point Input 3D point - */ - inline T - pruneStack(const Vec3& a_point) const noexcept; + @brief Polymorphic partitioner for splitting a list of primitives into K new + lists of primitives + @details P is the primitive type bound in the BVH and K is the BVH degree. + @param[in] a_primitives List of primitives to be subdivided into sub-bounding + volumes. + @return Returns a list (std::array) of new primitives which make up the new + bounding volumes. + */ + template + using PartitionerT = std::function, K>(const PrimitiveListT

& a_primitives)>; - /*! - @brief Recursively ordered pruning along the BVH tree. - @param[in,out] a_closest Shortest distance to primitives so far. - @param[in,out] a_point Input 3D point - */ - inline void - pruneOrdered(T& a_closest, const Vec3& a_point) const noexcept; - - /*! - @brief Recursive unordered pruning along the BVH tree. - @param[in,out] a_closest Shortest distance to primitives so far. - @param[in,out] a_point Input 3D point - */ - inline void - pruneUnordered(T& a_closest, const Vec3& a_point) const noexcept; - - /*! - @brief Flatten tree method. - @details This function will flatten everything beneath the current node and - linearize all the nodes and primitives beneath it to a_linearNodes and - a_sortedPrimitives. This function is called recursively. - @param[in,out] a_linearNodes BVH nodes, linearized onto a vector. - @param[in,out] a_sortedPrimitives Sorted primitives (in leaf node order). - @param[in,out] a_offset Supporting integer for figuring out where - in the tree we are. - @note When called from the root node, a_linearNodes and a_sortedPrimitives - should be empty and a_offset=0UL. - */ - inline size_t - flattenTree( - std::vector>>& a_linearNodes, - std::vector>& a_sortedPrimitives, - size_t& a_offset) const noexcept; -}; - -/*! - @brief Node type for linearized (flattened) BVH. This will be constructed from - the other (conventional) BVH type. - - @details T is the precision for Vec3, P is the primitive type you want to - enclose, BV is the bounding volume you use for it. - - @note P MUST supply function signedDistance(...) BV must supply a function - getDistance (had this been C++20, we would have use concepts to enforce this). - Note that LinearNode is the result of a flattened BVH hierarchy where nodes - are stored with depth-first ordering for improved cache-location in the - downward traversal. - - @note This class exists so that we can fit the nodes with a smaller memory - footprint. The standard BVH node (NodeT) is very useful when building the tree - but less useful when traversing it since it stores references to the - primitives in the node itself. It will span multiple cache lines. This node - exists so that we can fit all the BVH info onto fewer cache lines. The number - of cache lines will depend on the tree degree, precision, and bounding volume - that is chosen. - - @todo There's a minor optimization that can be made to the memory alignment, - which is as follows: For a leaf node we never really need the m_childOffsets - array, and for a regular node we never really need the m_primitivesOffset - member. Moreover, m_childOffsets could be made into a K-1 sized array because - we happen to know that the linearized hierarchy will store the first child - node immediately after the regular node. We could shave off 16 bytes of - storage, which would mean that a double-precision binary tree only takes up - one word of CPU memory. -*/ -template -class LinearNodeT -{ -public: - /*! - @brief Alias for cutting down on typing. - */ - using Vec3 = Vec3T; - - /*! - @brief Constructor. - */ - inline LinearNodeT() noexcept; - - /*! - @brief Destructor. - */ - inline virtual ~LinearNodeT(); - - /*! - @brief Set the bounding volume - @param[in] a_boundingVolume Bounding volume for this node. - */ - inline void - setBoundingVolume(const BV& a_boundingVolume) noexcept; - - /*! - @brief Set the offset into the primitives array. - */ - inline void - setPrimitivesOffset(const size_t a_primitivesOffset) noexcept; - - /*! - @brief Set number of primitives. - @param[in] a_numPrimitives Number of primitives. - */ - inline void - setNumPrimitives(const size_t a_numPrimitives) noexcept; - - /*! - @brief Set the child offsets. - @param[in] a_childOffset Offset in node array. - @param[in] a_whichChild Child index in m_childrenOffsets. Must be [0,K-1] - */ - inline void - setChildOffset(const size_t a_childOffset, const size_t a_whichChild) noexcept; - - /*! - @brief Get the node bounding volume. - return m_boundingVolume - */ - inline const BV& - getBoundingVolume() const noexcept; - - /*! - @brief Get the primitives offset - @return Returns m_primitivesOffset - */ - inline const size_t& - getPrimitivesOffset() const noexcept; - - /*! - @brief Get the number of primitives. - @return Returns m_numPrimitives - */ - inline const size_t& - getNumPrimitives() const noexcept; - - /*! - @brief Get the child offsets - @return Returns m_childOffsets - */ - inline const std::array& - getChildOffsets() const noexcept; - - /*! - @brief Is leaf or not - */ - inline bool - isLeaf() const noexcept; - - /*! - @brief Get the distance from a 3D point to the bounding volume - @param[in] a_point 3D point - @return Returns distance to bounding volume. A zero distance implies that - the input point is inside the bounding volume. - */ - inline T - getDistanceToBoundingVolume(const Vec3& a_point) const noexcept; - - /*! - @brief Compute signed distance to primitives. - @param[in] a_point Point - @param[in] a_primitives List of primitives - @note Only call if this is a leaf node. - */ - inline T - getDistanceToPrimitives( - const Vec3& a_point, const std::vector>& a_primitives) const noexcept; - -protected: - /*! - @brief Bounding volume. - */ - BV m_boundingVolume; - - /*! - @brief Offset into primitives array - */ - size_t m_primitivesOffset; - - /*! - @brief Number of primitives - */ - size_t m_numPrimitives; - - /*! - @brief Offset to child nodes. - */ - std::array m_childOffsets; -}; - -/*! - @brief Linear root node for BVH hierarchy -*/ -template -class LinearBVH : public SignedDistanceFunction -{ -public: - /*! - @brief Cut down on typing - */ - using Vec3 = Vec3T; - - /*! - @brief Alias for cutting down on typing - */ - using LinearNode = LinearNodeT; - - /*! - @brief List of primitives - */ - using PrimitiveList = std::vector>; - - /*! - @brief Disallowed. Use the full constructor please. - */ - LinearBVH() = delete; - - /*! - @brief Full constructor. Associates the nodes and primitives. - @param[in] a_linearNodes Linearized BVH nodes. - @param[in] a_primitives Primitives. - */ - inline LinearBVH( - const std::vector>>& a_linearNodes, - const std::vector>& a_primitives); - - /*! - @brief Full constructor. Associates the nodes and primitives. - @param[in] a_linearNodes Linearized BVH nodes. - @param[in] a_primitives Primitives. - */ - inline LinearBVH( - const std::vector>>& a_linearNodes, - const std::vector>& a_primitives); - - /*! - @brief Destructor. Does nothing - */ - inline virtual ~LinearBVH(); - - /*! - @brief Function which computes the signed distance. This calls the other - version. - @param[in] a_point 3D point in space - */ - inline T - signedDistance(const Vec3& a_point) const noexcept override; - -protected: - /*! - @brief List of linearly stored nodes - */ - std::vector>> m_linearNodes; - - /*! - @brief Global list of primitives. Note that this is ALL primitives, sorted - so that LinearNodeT can interface into it. - */ - std::vector> m_primitives; -}; + /*! + @brief Constructor method for creating bounding volumes from a list of + primitives + @details P is the primitive type bound in the BVH and BV is the bounding + volume type. + @param[in] a_primitives List of primitives. + @return Returns a new bounding volumes which is guaranteed to enclose all the + input primitives. + */ + template + using BVConstructorT = std::function& a_primitive)>; + + /*! + @brief Typename for identifying algorithms various algorithms during tree + traversel. + @details Stack => Use stack/priority queue (ordered traversal). + Ordered => Use recursive ordered traversal. + Unordered => Use recursive unordered traversal. + */ + enum class Prune + { + Stack, + Ordered, + Unordered, + }; + + /*! + @brief Class which encapsulates a node in a bounding volume hierarchy. + @details T is the precision, P is the primitive type you want to enclose, BV + is the bounding volume type used at the nodes. The parameter K (which must be + > 1) is the tree degree. K=2 is a binary tree, K=3 is a tertiary tree and so + on. + @note Template constraints are as following: + + P MUST supply function signedDistance(...) . + BV MUST supply a function getDistance + + Had this been C++20, we would have use concepts to enforce this. + */ + template + class NodeT : public SignedDistanceFunction + { + public: + /*! + @brief Alias for cutting down on typing. This is a + std::vector >. + */ + using PrimitiveList = PrimitiveListT

; + + /*! + @brief Alias for cutting down on typing. + */ + using Vec3 = Vec3T; + + /*! + @brief Alias for cutting down on typing. + */ + using Node = NodeT; + + /*! + @brief Alias for cutting down on typing. + */ + using NodePtr = std::shared_ptr; + + /*! + @brief Alias for cutting down on typing. + */ + using StopFunction = StopFunctionT; + + /*! + @brief Alias for cutting down on typing + */ + using Partitioner = PartitionerT; + + /*! + @brief Alias for cutting down on typing. + */ + using BVConstructor = BVConstructorT; + + /*! + @brief Default constructor which sets a regular node. + */ + NodeT(); + + /*! + @brief Construct node from a set of primitives. + @details This node becomes a leaf node which contains the input primitives. + @param[in] a_primitives Input primitives. + */ + NodeT(const std::vector>& a_primitives); + + /*! + @brief Construct node from a set of primitives. + @details This node becomes a leaf node which contains the input primitives. + @param[in] a_primitives Input primitives. + */ + NodeT(const std::vector>& a_primitives); + + /*! + @brief Destructor (does nothing) + */ + virtual ~NodeT(); + + /*! + @brief Function for using top-down construction of the bounding volume + hierarchy. + @details The rules for terminating the hierarchy construction, how to + partition sets of primitives, and how to enclose them by bounding volumes + are given in the input arguments (a_stopFunc, a_partFunc, a_bvFunc) + @param[in] a_bvConstructor Polymorphic function which builds a bounding + volume from a set of primitives. + @param[in] a_partitioner Partitioning function. This is a polymorphic + function which divides a set of primitives into two lists. + @param[in] a_stopCrit Termination function which tells us when to stop + the recursion. + */ + inline void + topDownSortAndPartitionPrimitives(const BVConstructor& a_bvConstructor, + const Partitioner& a_partitioner, + const StopFunction& a_stopCrit) noexcept; + + /*! + @brief Get node type + */ + inline bool + isLeaf() const noexcept; + + /*! + @brief Get the primitives stored in this node. + @return m_primitives. + */ + inline const PrimitiveList& + getPrimitives() const noexcept; + + /*! + @brief Get bounding volume + @return m_bv + */ + inline const BV& + getBoundingVolume() const noexcept; + + /*! + @brief Return this node's children. + @return m_children. + */ + inline const std::array>, K>& + getChildren() const noexcept; + + /*! + @brief Function which computes the signed distance + @param[in] a_point 3D point in space + @return Signed distance to the input point. + */ + inline T + signedDistance(const Vec3T& a_point) const noexcept override; + + /*! + @brief Function which computes the signed distance. This version allows the + user to manually select a traversal algorithm. + @param[in] a_point 3D point in space + @param[in] a_pruning Pruning algorithm + @return Signed distance to the input point. + */ + inline T + signedDistance(const Vec3T& a_point, const Prune a_pruning) const noexcept; + + /*! + @brief Flatten everything beneath this node into a depth-first sorted BVH + hierarchy. + @details This will compute the flattening of the standard BVH tree and + return a pointer to the linear corresponding to the current node. + */ + inline std::shared_ptr> + flattenTree() const noexcept; + + protected: + /*! + @brief Bounding volume object. + */ + BV m_boundingVolume; + + /*! + @brief Primitives list. This will be empty for regular nodes + */ + std::vector> m_primitives; + + /*! + @brief Children nodes + */ + std::array>, K> m_children; + + /*! + @brief Insert nodes with primitives. + @param[in] a_primitives Primitives for children. + */ + inline void + insertChildren(const std::array& a_primitives) noexcept; + + /*! + @brief Set primitives in this node + @param[in] a_primitives Primitives + */ + inline void + setPrimitives(const PrimitiveList& a_primitives) noexcept; + + /*! + @brief Get the distance from a 3D point to the bounding volume + @param[in] a_point 3D point + @return Returns distance to bounding volume. A zero distance implies that + the input point is inside the bounding volume. + */ + inline T + getDistanceToBoundingVolume(const Vec3& a_point) const noexcept; + + /*! + @brief Compute the shortest distance to the primitives in this node. + @param[in] a_point 3D point + @return Returns the signed distance to the primitives. + */ + inline T + getDistanceToPrimitives(const Vec3& a_point) const noexcept; + + /*! + @brief Get the list of primitives in this node. + @return Primitives list + */ + inline PrimitiveList& + getPrimitives() noexcept; + + /*! + @brief Iterative ordered pruning along the BVH tree. + @param[in,out] a_point Input 3D point + */ + inline T + pruneStack(const Vec3& a_point) const noexcept; + + /*! + @brief Recursively ordered pruning along the BVH tree. + @param[in,out] a_closest Shortest distance to primitives so far. + @param[in,out] a_point Input 3D point + */ + inline void + pruneOrdered(T& a_closest, const Vec3& a_point) const noexcept; + + /*! + @brief Recursive unordered pruning along the BVH tree. + @param[in,out] a_closest Shortest distance to primitives so far. + @param[in,out] a_point Input 3D point + */ + inline void + pruneUnordered(T& a_closest, const Vec3& a_point) const noexcept; + + /*! + @brief Flatten tree method. + @details This function will flatten everything beneath the current node and + linearize all the nodes and primitives beneath it to a_linearNodes and + a_sortedPrimitives. This function is called recursively. + @param[in,out] a_linearNodes BVH nodes, linearized onto a vector. + @param[in,out] a_sortedPrimitives Sorted primitives (in leaf node order). + @param[in,out] a_offset Supporting integer for figuring out where + in the tree we are. + @note When called from the root node, a_linearNodes and a_sortedPrimitives + should be empty and a_offset=0UL. + */ + inline size_t + flattenTree(std::vector>>& a_linearNodes, + std::vector>& a_sortedPrimitives, + size_t& a_offset) const noexcept; + }; + + /*! + @brief Node type for linearized (flattened) BVH. This will be constructed from + the other (conventional) BVH type. + + @details T is the precision for Vec3, P is the primitive type you want to + enclose, BV is the bounding volume you use for it. + + @note P MUST supply function signedDistance(...) BV must supply a function + getDistance (had this been C++20, we would have use concepts to enforce this). + Note that LinearNode is the result of a flattened BVH hierarchy where nodes + are stored with depth-first ordering for improved cache-location in the + downward traversal. + + @note This class exists so that we can fit the nodes with a smaller memory + footprint. The standard BVH node (NodeT) is very useful when building the tree + but less useful when traversing it since it stores references to the + primitives in the node itself. It will span multiple cache lines. This node + exists so that we can fit all the BVH info onto fewer cache lines. The number + of cache lines will depend on the tree degree, precision, and bounding volume + that is chosen. + + @todo There's a minor optimization that can be made to the memory alignment, + which is as follows: For a leaf node we never really need the m_childOffsets + array, and for a regular node we never really need the m_primitivesOffset + member. Moreover, m_childOffsets could be made into a K-1 sized array because + we happen to know that the linearized hierarchy will store the first child + node immediately after the regular node. We could shave off 16 bytes of + storage, which would mean that a double-precision binary tree only takes up + one word of CPU memory. + */ + template + class LinearNodeT + { + public: + /*! + @brief Alias for cutting down on typing. + */ + using Vec3 = Vec3T; + + /*! + @brief Constructor. + */ + inline LinearNodeT() noexcept; + + /*! + @brief Destructor. + */ + inline virtual ~LinearNodeT(); + + /*! + @brief Set the bounding volume + @param[in] a_boundingVolume Bounding volume for this node. + */ + inline void + setBoundingVolume(const BV& a_boundingVolume) noexcept; + + /*! + @brief Set the offset into the primitives array. + */ + inline void + setPrimitivesOffset(const size_t a_primitivesOffset) noexcept; + + /*! + @brief Set number of primitives. + @param[in] a_numPrimitives Number of primitives. + */ + inline void + setNumPrimitives(const size_t a_numPrimitives) noexcept; + + /*! + @brief Set the child offsets. + @param[in] a_childOffset Offset in node array. + @param[in] a_whichChild Child index in m_childrenOffsets. Must be [0,K-1] + */ + inline void + setChildOffset(const size_t a_childOffset, const size_t a_whichChild) noexcept; + + /*! + @brief Get the node bounding volume. + return m_boundingVolume + */ + inline const BV& + getBoundingVolume() const noexcept; + + /*! + @brief Get the primitives offset + @return Returns m_primitivesOffset + */ + inline const size_t& + getPrimitivesOffset() const noexcept; + + /*! + @brief Get the number of primitives. + @return Returns m_numPrimitives + */ + inline const size_t& + getNumPrimitives() const noexcept; + + /*! + @brief Get the child offsets + @return Returns m_childOffsets + */ + inline const std::array& + getChildOffsets() const noexcept; + + /*! + @brief Is leaf or not + */ + inline bool + isLeaf() const noexcept; + + /*! + @brief Get the distance from a 3D point to the bounding volume + @param[in] a_point 3D point + @return Returns distance to bounding volume. A zero distance implies that + the input point is inside the bounding volume. + */ + inline T + getDistanceToBoundingVolume(const Vec3& a_point) const noexcept; + + /*! + @brief Compute signed distance to primitives. + @param[in] a_point Point + @param[in] a_primitives List of primitives + @note Only call if this is a leaf node. + */ + inline T + getDistanceToPrimitives(const Vec3& a_point, + const std::vector>& a_primitives) const noexcept; + + protected: + /*! + @brief Bounding volume. + */ + BV m_boundingVolume; + + /*! + @brief Offset into primitives array + */ + size_t m_primitivesOffset; + + /*! + @brief Number of primitives + */ + size_t m_numPrimitives; + + /*! + @brief Offset to child nodes. + */ + std::array m_childOffsets; + }; + + /*! + @brief Linear root node for BVH hierarchy + */ + template + class LinearBVH : public SignedDistanceFunction + { + public: + /*! + @brief Cut down on typing + */ + using Vec3 = Vec3T; + + /*! + @brief Alias for cutting down on typing + */ + using LinearNode = LinearNodeT; + + /*! + @brief List of primitives + */ + using PrimitiveList = std::vector>; + + /*! + @brief Disallowed. Use the full constructor please. + */ + LinearBVH() = delete; + + /*! + @brief Full constructor. Associates the nodes and primitives. + @param[in] a_linearNodes Linearized BVH nodes. + @param[in] a_primitives Primitives. + */ + inline LinearBVH(const std::vector>>& a_linearNodes, + const std::vector>& a_primitives); + + /*! + @brief Full constructor. Associates the nodes and primitives. + @param[in] a_linearNodes Linearized BVH nodes. + @param[in] a_primitives Primitives. + */ + inline LinearBVH(const std::vector>>& a_linearNodes, + const std::vector>& a_primitives); + + /*! + @brief Destructor. Does nothing + */ + inline virtual ~LinearBVH(); + + /*! + @brief Function which computes the signed distance. This calls the other + version. + @param[in] a_point 3D point in space + */ + inline T + signedDistance(const Vec3& a_point) const noexcept override; + + protected: + /*! + @brief List of linearly stored nodes + */ + std::vector>> m_linearNodes; + + /*! + @brief Global list of primitives. Note that this is ALL primitives, sorted + so that LinearNodeT can interface into it. + */ + std::vector> m_primitives; + }; } // namespace BVH #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_BVHImplem.hpp b/Source/EBGeometry_BVHImplem.hpp index c39f889d..4e78926f 100644 --- a/Source/EBGeometry_BVHImplem.hpp +++ b/Source/EBGeometry_BVHImplem.hpp @@ -21,611 +21,614 @@ namespace BVH { -template -inline NodeT::NodeT() -{ - for (auto& c : m_children) { - c = nullptr; - } - - m_primitives.resize(0); -} - -template -inline NodeT::NodeT(const std::vector>& a_primitives) : NodeT() -{ - for (const auto& p : a_primitives) { - m_primitives.emplace_back(p); - } - - for (auto& c : m_children) { - c = nullptr; - } -} - -template -inline NodeT::NodeT(const std::vector>& a_primitives) : NodeT() -{ - m_primitives = a_primitives; - - for (auto& c : m_children) { - c = nullptr; - } -} - -template -inline NodeT::~NodeT() -{ -} - -template -inline void -NodeT::setPrimitives(const PrimitiveList& a_primitives) noexcept -{ - m_primitives = a_primitives; -} - -template -inline bool -NodeT::isLeaf() const noexcept -{ - return m_primitives.size() > 0; -} - -template -inline PrimitiveListT

& -NodeT::getPrimitives() noexcept -{ - return (m_primitives); -} - -template -inline const PrimitiveListT

& -NodeT::getPrimitives() const noexcept -{ - return (m_primitives); -} - -template -inline const BV& -NodeT::getBoundingVolume() const noexcept -{ - return (m_boundingVolume); -} - -template -inline const std::array>, K>& -NodeT::getChildren() const noexcept -{ - return (m_children); -} - -template -inline void -NodeT::topDownSortAndPartitionPrimitives( - const BVConstructor& a_bvConstructor, const Partitioner& a_partitioner, const StopFunction& a_stopCrit) noexcept -{ - - // Compute the bounding volume for this node. - std::vector boundingVolumes; - for (const auto& p : m_primitives) { - boundingVolumes.emplace_back(a_bvConstructor(p)); - } - - m_boundingVolume = BV(boundingVolumes); - - // Check if we can split this node into sub-bounding volumes. - const bool stopRecursiveSplitting = a_stopCrit(*this); - const bool hasEnoughPrimitives = m_primitives.size() >= K; - - if (!stopRecursiveSplitting && hasEnoughPrimitives) { - - // Divide primitives into new partitions - const auto& newPartitions = a_partitioner(m_primitives); // Divide this node's primitives into K new - // sub-volume primitives - this->insertChildren(newPartitions); // Insert the K new nodes into the tree. - - m_primitives.resize(0); // This node is no longer a leaf node. + template + inline NodeT::NodeT() + { + for (auto& c : m_children) { + c = nullptr; + } + + m_primitives.resize(0); + } + + template + inline NodeT::NodeT(const std::vector>& a_primitives) : NodeT() + { + for (const auto& p : a_primitives) { + m_primitives.emplace_back(p); + } - // Partition children nodes further for (auto& c : m_children) { - c->topDownSortAndPartitionPrimitives(a_bvConstructor, a_partitioner, a_stopCrit); + c = nullptr; } } -} -template -inline void -NodeT::insertChildren(const std::array& a_primitives) noexcept -{ - for (size_t l = 0; l < K; l++) { - m_children[l] = std::make_shared>(); + template + inline NodeT::NodeT(const std::vector>& a_primitives) : NodeT() + { + m_primitives = a_primitives; - m_children[l]->setPrimitives(a_primitives[l]); + for (auto& c : m_children) { + c = nullptr; + } } -} -template -inline T -NodeT::getDistanceToBoundingVolume(const Vec3& a_point) const noexcept -{ - return m_boundingVolume.getDistance(a_point); -} + template + inline NodeT::~NodeT() + {} -template -inline T -NodeT::getDistanceToPrimitives(const Vec3& a_point) const noexcept -{ - T minDist = std::numeric_limits::infinity(); + template + inline void + NodeT::setPrimitives(const PrimitiveList& a_primitives) noexcept + { + m_primitives = a_primitives; + } - for (const auto& p : m_primitives) { - const auto curDist = p->signedDistance(a_point); + template + inline bool + NodeT::isLeaf() const noexcept + { + return m_primitives.size() > 0; + } - if (curDist * curDist < minDist * minDist) { - minDist = curDist; - } + template + inline PrimitiveListT

& + NodeT::getPrimitives() noexcept + { + return (m_primitives); } - return minDist; -} + template + inline const PrimitiveListT

& + NodeT::getPrimitives() const noexcept + { + return (m_primitives); + } + + template + inline const BV& + NodeT::getBoundingVolume() const noexcept + { + return (m_boundingVolume); + } + + template + inline const std::array>, K>& + NodeT::getChildren() const noexcept + { + return (m_children); + } -template -inline T -NodeT::signedDistance(const Vec3& a_point) const noexcept -{ - return this->signedDistance(a_point, Prune::Stack); -} + template + inline void + NodeT::topDownSortAndPartitionPrimitives(const BVConstructor& a_bvConstructor, + const Partitioner& a_partitioner, + const StopFunction& a_stopCrit) noexcept + { + + // Compute the bounding volume for this node. + std::vector boundingVolumes; + for (const auto& p : m_primitives) { + boundingVolumes.emplace_back(a_bvConstructor(p)); + } + + m_boundingVolume = BV(boundingVolumes); + + // Check if we can split this node into sub-bounding volumes. + const bool stopRecursiveSplitting = a_stopCrit(*this); + const bool hasEnoughPrimitives = m_primitives.size() >= K; -template -inline T -NodeT::signedDistance(const Vec3& a_point, const Prune a_pruning) const noexcept -{ - T ret = std::numeric_limits::infinity(); + if (!stopRecursiveSplitting && hasEnoughPrimitives) { - switch (a_pruning) { - case Prune::Stack: { - ret = this->pruneStack(a_point); + // Divide primitives into new partitions + const auto& newPartitions = a_partitioner(m_primitives); // Divide this node's primitives into K new + // sub-volume primitives + this->insertChildren(newPartitions); // Insert the K new nodes into the tree. - break; + m_primitives.resize(0); // This node is no longer a leaf node. + + // Partition children nodes further + for (auto& c : m_children) { + c->topDownSortAndPartitionPrimitives(a_bvConstructor, a_partitioner, a_stopCrit); + } + } } - case Prune::Ordered: { - this->pruneOrdered(ret, a_point); - break; + template + inline void + NodeT::insertChildren(const std::array& a_primitives) noexcept + { + for (size_t l = 0; l < K; l++) { + m_children[l] = std::make_shared>(); + + m_children[l]->setPrimitives(a_primitives[l]); + } } - case Prune::Unordered: { - this->pruneUnordered(ret, a_point); - break; + template + inline T + NodeT::getDistanceToBoundingVolume(const Vec3& a_point) const noexcept + { + return m_boundingVolume.getDistance(a_point); } - default: - std::cerr << "In file EBGeometry_BVHImplem.hpp function NodeT::signedDistance(Vec3, Prune) -- bad input enum for 'Prune'\n"; - }; - return ret; -} + template + inline T + NodeT::getDistanceToPrimitives(const Vec3& a_point) const noexcept + { + T minDist = std::numeric_limits::infinity(); -template -inline T -NodeT::pruneStack(const Vec3& a_point) const noexcept -{ - // TLDR: This routine uses ordered traversal along the branches. Rather than - // calling itself recursively, it uses - // a stack for investigating the branches and nodes. + for (const auto& p : m_primitives) { + const auto curDist = p->signedDistance(a_point); - // Shortest distance. Initialize to something big. - T minDist = std::numeric_limits::infinity(); + if (curDist * curDist < minDist * minDist) { + minDist = curDist; + } + } - // Create temporary storage and and priority queue (our stack). - using RawNode = const NodeT*; - using NodeAndDist = std::pair; + return minDist; + } - std::array childrenAndDistances; - std::stack q; + template + inline T + NodeT::signedDistance(const Vec3& a_point) const noexcept + { + return this->signedDistance(a_point, Prune::Stack); + } - // Initialize the stack with the root node. - q.emplace(this, this->getDistanceToBoundingVolume(a_point)); + template + inline T + NodeT::signedDistance(const Vec3& a_point, const Prune a_pruning) const noexcept + { + T ret = std::numeric_limits::infinity(); - // Stack loop -- always investigate the one at the top. - while (!(q.empty())) { + switch (a_pruning) { + case Prune::Stack: { + ret = this->pruneStack(a_point); - // Pop the top node off the stack. - const auto& curNode = (q.top()).first; - const auto& bvDist = (q.top()).second; + break; + } + case Prune::Ordered: { + this->pruneOrdered(ret, a_point); - q.pop(); + break; + } + case Prune::Unordered: { + this->pruneUnordered(ret, a_point); - // See if we really need to process this node. We only need to do it if its - // BV is closer than the shortest distance we've found so far. Otherwise we - // are guaranteed that the distance to the primitives is larger than the - // shortest distance we've found so far. If the current node is a leaf node - // we just update the shortest distance. Otherwise we fetch the child nodes - // and process them (in a very specific order!). - if (bvDist <= std::abs(minDist)) { - if (curNode->isLeaf()) { - const T primDist = curNode->getDistanceToPrimitives(a_point); + break; + } + default: + std::cerr << "In file EBGeometry_BVHImplem.hpp function NodeT::signedDistance(Vec3, Prune) -- bad input enum for 'Prune'\n"; + }; - if (std::abs(primDist) < std::abs(minDist)) { - minDist = primDist; + return ret; + } + + template + inline T + NodeT::pruneStack(const Vec3& a_point) const noexcept + { + // TLDR: This routine uses ordered traversal along the branches. Rather than + // calling itself recursively, it uses + // a stack for investigating the branches and nodes. + + // Shortest distance. Initialize to something big. + T minDist = std::numeric_limits::infinity(); + + // Create temporary storage and and priority queue (our stack). + using RawNode = const NodeT*; + using NodeAndDist = std::pair; + + std::array childrenAndDistances; + std::stack q; + + // Initialize the stack with the root node. + q.emplace(this, this->getDistanceToBoundingVolume(a_point)); + + // Stack loop -- always investigate the one at the top. + while (!(q.empty())) { + + // Pop the top node off the stack. + const auto& curNode = (q.top()).first; + const auto& bvDist = (q.top()).second; + + q.pop(); + + // See if we really need to process this node. We only need to do it if its + // BV is closer than the shortest distance we've found so far. Otherwise we + // are guaranteed that the distance to the primitives is larger than the + // shortest distance we've found so far. If the current node is a leaf node + // we just update the shortest distance. Otherwise we fetch the child nodes + // and process them (in a very specific order!). + if (bvDist <= std::abs(minDist)) { + if (curNode->isLeaf()) { + const T primDist = curNode->getDistanceToPrimitives(a_point); + + if (std::abs(primDist) < std::abs(minDist)) { + minDist = primDist; + } } - } else { - // If it's a regular node, sort the child nodes and put them on the - // stack. On the next iteration we do the closest node first. This - // sorting is critical to the performance of the BVH. + else { + // If it's a regular node, sort the child nodes and put them on the + // stack. On the next iteration we do the closest node first. This + // sorting is critical to the performance of the BVH. - // Get the nodes's children. - const std::array& children = curNode->getChildren(); + // Get the nodes's children. + const std::array& children = curNode->getChildren(); - for (size_t k = 0; k < K; k++) { - const RawNode& child = &(*children[k]); + for (size_t k = 0; k < K; k++) { + const RawNode& child = &(*children[k]); - childrenAndDistances[k] = std::make_pair(child, child->getDistanceToBoundingVolume(a_point)); - } + childrenAndDistances[k] = std::make_pair(child, child->getDistanceToBoundingVolume(a_point)); + } - std::sort( - childrenAndDistances.begin(), childrenAndDistances.end(), - [](const NodeAndDist& node1, const NodeAndDist& node2) -> bool { return node1.second > node2.second; }); + std::sort( + childrenAndDistances.begin(), + childrenAndDistances.end(), + [](const NodeAndDist& node1, const NodeAndDist& node2) -> bool { return node1.second > node2.second; }); - // Push the children onto the stack. - for (const auto& child : childrenAndDistances) { - q.push(child); + // Push the children onto the stack. + for (const auto& child : childrenAndDistances) { + q.push(child); + } } } } + + return minDist; } - return minDist; -} - -template -inline void -NodeT::pruneOrdered(T& a_shortestDistanceSoFar, const Vec3& a_point) const noexcept -{ - - // TLDR: Beginning at some node, this routine descends the branches in the - // tree. It always descends the branch with the shortest distance - // to the bounding volume first. The other branch is investigated only - // after the full sub-tree beneath the first branch has completed. Since - // the shortest distance to primitives is updated underway, there is a - // decent chance that the secondary subtree can be pruned. Hence why - // this routine is more efficient than prunedUnordered. - if (this->isLeaf()) { - // Compute the shortest signed distance to the primitives in this leaf node. - // If this is shorter than a_shortestDistanceSoFar, update it. Recall that - // the comparison requires the absolute value since we're doing the SIGNED - // distance. - const T primDist = this->getDistanceToPrimitives(a_point); - - if (std::abs(primDist) < std::abs(a_shortestDistanceSoFar)) { - a_shortestDistanceSoFar = primDist; - } - } else { - // In this case we need to decide which subtree to move down. First, sort - // the children nodes by the distance between a_point and the children - // node's bounding volume. Shortest distance goes first. - std::array, K> distancesAndNodes; - for (size_t i = 0; i < K; i++) { - distancesAndNodes[i] = std::make_pair(m_children[i]->getDistanceToBoundingVolume(a_point), m_children[i]); + template + inline void + NodeT::pruneOrdered(T& a_shortestDistanceSoFar, const Vec3& a_point) const noexcept + { + + // TLDR: Beginning at some node, this routine descends the branches in the + // tree. It always descends the branch with the shortest distance + // to the bounding volume first. The other branch is investigated only + // after the full sub-tree beneath the first branch has completed. Since + // the shortest distance to primitives is updated underway, there is a + // decent chance that the secondary subtree can be pruned. Hence why + // this routine is more efficient than prunedUnordered. + if (this->isLeaf()) { + // Compute the shortest signed distance to the primitives in this leaf node. + // If this is shorter than a_shortestDistanceSoFar, update it. Recall that + // the comparison requires the absolute value since we're doing the SIGNED + // distance. + const T primDist = this->getDistanceToPrimitives(a_point); + + if (std::abs(primDist) < std::abs(a_shortestDistanceSoFar)) { + a_shortestDistanceSoFar = primDist; + } } + else { + // In this case we need to decide which subtree to move down. First, sort + // the children nodes by the distance between a_point and the children + // node's bounding volume. Shortest distance goes first. + std::array, K> distancesAndNodes; + for (size_t i = 0; i < K; i++) { + distancesAndNodes[i] = std::make_pair(m_children[i]->getDistanceToBoundingVolume(a_point), m_children[i]); + } - // Comparator for sorting -- puts the node with the shortest distance to the - // bounding volume at the front of the vector. - auto comparator = [](const std::pair& a_node1, const std::pair& a_node2) -> bool { - return std::abs(a_node1.first) < std::abs(a_node2.first); - }; + // Comparator for sorting -- puts the node with the shortest distance to the + // bounding volume at the front of the vector. + auto comparator = [](const std::pair& a_node1, const std::pair& a_node2) -> bool { + return std::abs(a_node1.first) < std::abs(a_node2.first); + }; - std::sort(distancesAndNodes.begin(), distancesAndNodes.end(), comparator); + std::sort(distancesAndNodes.begin(), distancesAndNodes.end(), comparator); - // Go through the children nodes -- closest node goes first. We prune - // branches if the distance to the node's bounding volume is longer than the - // shortest distance we've found so far. - for (size_t i = 0; i < K; i++) { - const std::pair& curChildNode = distancesAndNodes[i]; + // Go through the children nodes -- closest node goes first. We prune + // branches if the distance to the node's bounding volume is longer than the + // shortest distance we've found so far. + for (size_t i = 0; i < K; i++) { + const std::pair& curChildNode = distancesAndNodes[i]; - // a_shortestDistanceSoFar is the SIGNED distance, so we need the absolute - // value here. - if (std::abs(curChildNode.first) <= std::abs(a_shortestDistanceSoFar)) { - curChildNode.second->pruneOrdered(a_shortestDistanceSoFar, a_point); - } else { // Prune the rest of the children nodes. - break; + // a_shortestDistanceSoFar is the SIGNED distance, so we need the absolute + // value here. + if (std::abs(curChildNode.first) <= std::abs(a_shortestDistanceSoFar)) { + curChildNode.second->pruneOrdered(a_shortestDistanceSoFar, a_point); + } + else { // Prune the rest of the children nodes. + break; + } } } } -} -template -inline void -NodeT::pruneUnordered(T& a_shortestDistanceSoFar, const Vec3& a_point) const noexcept -{ - if (this->isLeaf()) { - // Check if the distance to the primitives in this leaf is shorter than - // a_shortestDistanceSoFar. If it is, update the shortest distance. - const T curSignedDistance = this->getDistanceToPrimitives(a_point); - - if (std::abs(curSignedDistance) < std::abs(a_shortestDistanceSoFar)) { - a_shortestDistanceSoFar = curSignedDistance; + template + inline void + NodeT::pruneUnordered(T& a_shortestDistanceSoFar, const Vec3& a_point) const noexcept + { + if (this->isLeaf()) { + // Check if the distance to the primitives in this leaf is shorter than + // a_shortestDistanceSoFar. If it is, update the shortest distance. + const T curSignedDistance = this->getDistanceToPrimitives(a_point); + + if (std::abs(curSignedDistance) < std::abs(a_shortestDistanceSoFar)) { + a_shortestDistanceSoFar = curSignedDistance; + } } - } else { - // Investigate subtrees. Prune subtrees if the distance to their bounding - // volumes are longer than the shortest distance we've found so far. - for (const auto& child : m_children) { - const T distanceToChildBoundingVolume = child->getDistanceToBoundingVolume(a_point); - - if (std::abs(distanceToChildBoundingVolume) < std::abs(a_shortestDistanceSoFar)) { - child->pruneUnordered(a_shortestDistanceSoFar, a_point); + else { + // Investigate subtrees. Prune subtrees if the distance to their bounding + // volumes are longer than the shortest distance we've found so far. + for (const auto& child : m_children) { + const T distanceToChildBoundingVolume = child->getDistanceToBoundingVolume(a_point); + + if (std::abs(distanceToChildBoundingVolume) < std::abs(a_shortestDistanceSoFar)) { + child->pruneUnordered(a_shortestDistanceSoFar, a_point); + } } } } -} -template -inline std::shared_ptr> -NodeT::flattenTree() const noexcept -{ + template + inline std::shared_ptr> + NodeT::flattenTree() const noexcept + { - // Create a list of sorted primitives and nodes. - std::vector> sortedPrimitives; - std::vector>> linearNodes; + // Create a list of sorted primitives and nodes. + std::vector> sortedPrimitives; + std::vector>> linearNodes; - // Track the offset into the linearized node array. - size_t offset = 0; + // Track the offset into the linearized node array. + size_t offset = 0; - // Flatten recursively. - this->flattenTree(linearNodes, sortedPrimitives, offset); + // Flatten recursively. + this->flattenTree(linearNodes, sortedPrimitives, offset); - // Return the root node. - return std::make_shared>(linearNodes, sortedPrimitives); -} + // Return the root node. + return std::make_shared>(linearNodes, sortedPrimitives); + } -template -inline size_t -NodeT::flattenTree( - std::vector>>& a_linearNodes, - std::vector>& a_sortedPrimitives, - size_t& a_offset) const noexcept -{ + template + inline size_t + NodeT::flattenTree(std::vector>>& a_linearNodes, + std::vector>& a_sortedPrimitives, + size_t& a_offset) const noexcept + { - // TLDR: This is the main routine for flattening the hierarchy beneath the - // current node. When this is called we insert - // this node into a_linearNodes and associate the array offsets so that - // we can find the children in the linearized array. + // TLDR: This is the main routine for flattening the hierarchy beneath the + // current node. When this is called we insert + // this node into a_linearNodes and associate the array offsets so that + // we can find the children in the linearized array. - // Current node we are dealing with. - const auto curNode = a_offset; + // Current node we are dealing with. + const auto curNode = a_offset; - // Insert a new node corresponding to this node and provide it with the - // current bounding volume. - a_linearNodes.emplace_back(std::make_shared>()); - a_linearNodes[curNode]->setBoundingVolume(m_boundingVolume); + // Insert a new node corresponding to this node and provide it with the + // current bounding volume. + a_linearNodes.emplace_back(std::make_shared>()); + a_linearNodes[curNode]->setBoundingVolume(m_boundingVolume); - a_offset++; + a_offset++; - if (this->isLeaf()) { - // Insert primitives and offsets. - a_linearNodes[curNode]->setNumPrimitives(m_primitives.size()); - a_linearNodes[curNode]->setPrimitivesOffset(a_sortedPrimitives.size()); + if (this->isLeaf()) { + // Insert primitives and offsets. + a_linearNodes[curNode]->setNumPrimitives(m_primitives.size()); + a_linearNodes[curNode]->setPrimitivesOffset(a_sortedPrimitives.size()); - a_sortedPrimitives.insert(a_sortedPrimitives.end(), m_primitives.begin(), m_primitives.end()); - } else { - a_linearNodes[curNode]->setNumPrimitives(0); - a_linearNodes[curNode]->setPrimitivesOffset(0UL); + a_sortedPrimitives.insert(a_sortedPrimitives.end(), m_primitives.begin(), m_primitives.end()); + } + else { + a_linearNodes[curNode]->setNumPrimitives(0); + a_linearNodes[curNode]->setPrimitivesOffset(0UL); - // Go through the children nodes and - for (size_t k = 0; k < K; k++) { - const size_t offset = m_children[k]->flattenTree(a_linearNodes, a_sortedPrimitives, a_offset); + // Go through the children nodes and + for (size_t k = 0; k < K; k++) { + const size_t offset = m_children[k]->flattenTree(a_linearNodes, a_sortedPrimitives, a_offset); - a_linearNodes[curNode]->setChildOffset(offset, k); + a_linearNodes[curNode]->setChildOffset(offset, k); + } } + + return curNode; } - return curNode; -} - -template -inline LinearNodeT::LinearNodeT() noexcept -{ - - // Initialize everything. - m_boundingVolume = BV(); - m_primitivesOffset = 0UL; - m_numPrimitives = 0; - - for (auto& offset : m_childOffsets) { - offset = 0UL; - } -} - -template -inline LinearNodeT::~LinearNodeT() -{ -} - -template -inline void -LinearNodeT::setBoundingVolume(const BV& a_boundingVolume) noexcept -{ - m_boundingVolume = a_boundingVolume; -} - -template -inline void -LinearNodeT::setPrimitivesOffset(const size_t a_primitivesOffset) noexcept -{ - m_primitivesOffset = a_primitivesOffset; -} - -template -inline void -LinearNodeT::setNumPrimitives(const size_t a_numPrimitives) noexcept -{ - m_numPrimitives = a_numPrimitives; -} - -template -inline void -LinearNodeT::setChildOffset(const size_t a_childOffset, const size_t a_whichChild) noexcept -{ - m_childOffsets[a_whichChild] = a_childOffset; -} - -template -inline const BV& -LinearNodeT::getBoundingVolume() const noexcept -{ - return (m_boundingVolume); -} - -template -inline const size_t& -LinearNodeT::getPrimitivesOffset() const noexcept -{ - return (m_primitivesOffset); -} - -template -inline const size_t& -LinearNodeT::getNumPrimitives() const noexcept -{ - return (m_numPrimitives); -} - -template -inline const std::array& -LinearNodeT::getChildOffsets() const noexcept -{ - return (m_childOffsets); -} - -template -inline bool -LinearNodeT::isLeaf() const noexcept -{ - return m_numPrimitives > 0; -} - -template -inline T -LinearNodeT::getDistanceToBoundingVolume(const Vec3& a_point) const noexcept -{ - return m_boundingVolume.getDistance(a_point); -} - -template -inline T -LinearNodeT::getDistanceToPrimitives( - const Vec3T& a_point, const std::vector>& a_primitives) const noexcept -{ - T minDist = std::numeric_limits::infinity(); - - for (size_t i = 0; i < m_numPrimitives; i++) { - const T curDist = a_primitives[m_primitivesOffset + i]->signedDistance(a_point); - - if (std::abs(curDist) < std::abs(minDist)) { - minDist = curDist; + template + inline LinearNodeT::LinearNodeT() noexcept + { + + // Initialize everything. + m_boundingVolume = BV(); + m_primitivesOffset = 0UL; + m_numPrimitives = 0; + + for (auto& offset : m_childOffsets) { + offset = 0UL; } } - return minDist; -} + template + inline LinearNodeT::~LinearNodeT() + {} -template -inline LinearBVH::LinearBVH( - const std::vector>>& a_linearNodes, - const std::vector>& a_primitives) -{ - m_linearNodes = a_linearNodes; - m_primitives = a_primitives; -} + template + inline void + LinearNodeT::setBoundingVolume(const BV& a_boundingVolume) noexcept + { + m_boundingVolume = a_boundingVolume; + } -template -inline LinearBVH::LinearBVH( - const std::vector>>& a_linearNodes, - const std::vector>& a_primitives) -{ + template + inline void + LinearNodeT::setPrimitivesOffset(const size_t a_primitivesOffset) noexcept + { + m_primitivesOffset = a_primitivesOffset; + } - for (const auto& p : a_linearNodes) { - m_linearNodes.emplace_back(p); + template + inline void + LinearNodeT::setNumPrimitives(const size_t a_numPrimitives) noexcept + { + m_numPrimitives = a_numPrimitives; } - m_primitives = a_primitives; -} + template + inline void + LinearNodeT::setChildOffset(const size_t a_childOffset, const size_t a_whichChild) noexcept + { + m_childOffsets[a_whichChild] = a_childOffset; + } -template -inline LinearBVH::~LinearBVH() -{ -} + template + inline const BV& + LinearNodeT::getBoundingVolume() const noexcept + { + return (m_boundingVolume); + } -template -inline T -LinearBVH::signedDistance(const Vec3& a_point) const noexcept -{ - // TLDR: This routine uses ordered traversal along the branches. Rather than - // calling itself recursively, it uses - // a stack for investigating the branches and nodes. + template + inline const size_t& + LinearNodeT::getPrimitivesOffset() const noexcept + { + return (m_primitivesOffset); + } - // Shortest unsigned square distance. Initialize to something big. - T minDist = std::numeric_limits::infinity(); + template + inline const size_t& + LinearNodeT::getNumPrimitives() const noexcept + { + return (m_numPrimitives); + } - // Create temporary storage and and priority queue (our stack). - using NodeAndDist = std::pair; + template + inline const std::array& + LinearNodeT::getChildOffsets() const noexcept + { + return (m_childOffsets); + } - std::array children; - std::stack q; + template + inline bool + LinearNodeT::isLeaf() const noexcept + { + return m_numPrimitives > 0; + } - // Initialize the stack with the root node. - q.emplace(0, m_linearNodes[0]->getDistanceToBoundingVolume(a_point)); + template + inline T + LinearNodeT::getDistanceToBoundingVolume(const Vec3& a_point) const noexcept + { + return m_boundingVolume.getDistance(a_point); + } - // Stack loop -- always investigate the one at the top. - while (!(q.empty())) { + template + inline T + LinearNodeT::getDistanceToPrimitives( + const Vec3T& a_point, const std::vector>& a_primitives) const noexcept + { + T minDist = std::numeric_limits::infinity(); - // Pop the top node off the stack. - const auto& curNode = (q.top()).first; - const auto& bvDist = (q.top()).second; + for (size_t i = 0; i < m_numPrimitives; i++) { + const T curDist = a_primitives[m_primitivesOffset + i]->signedDistance(a_point); - q.pop(); + if (std::abs(curDist) < std::abs(minDist)) { + minDist = curDist; + } + } - // See if we really need to process this node. We only need to do it if its - // BV is closer than the shortest distance we've found so far. Otherwise we - // are guaranteed that the distance to the primitives is larger than the - // shortest distance we've found so far. - if (bvDist <= std::abs(minDist)) { + return minDist; + } - // If it's a leaf node, update the shortest distance so far. - if (m_linearNodes[curNode]->isLeaf()) { - const T primDist = m_linearNodes[curNode]->getDistanceToPrimitives(a_point, m_primitives); + template + inline LinearBVH::LinearBVH( + const std::vector>>& a_linearNodes, + const std::vector>& a_primitives) + { + m_linearNodes = a_linearNodes; + m_primitives = a_primitives; + } - if (std::abs(primDist) < std::abs(minDist)) { - minDist = primDist; - } - } else { - // Compute child indices and their BVH distance to a_point. - for (size_t k = 0; k < K; k++) { - const size_t& curOff = m_linearNodes[curNode]->getChildOffsets()[k]; - const T distanceToBV = m_linearNodes[curOff]->getDistanceToBoundingVolume(a_point); + template + inline LinearBVH::LinearBVH(const std::vector>>& a_linearNodes, + const std::vector>& a_primitives) + { - children[k] = std::make_pair(curOff, distanceToBV); - } + for (const auto& p : a_linearNodes) { + m_linearNodes.emplace_back(p); + } + + m_primitives = a_primitives; + } + + template + inline LinearBVH::~LinearBVH() + {} + + template + inline T + LinearBVH::signedDistance(const Vec3& a_point) const noexcept + { + // TLDR: This routine uses ordered traversal along the branches. Rather than + // calling itself recursively, it uses + // a stack for investigating the branches and nodes. + + // Shortest unsigned square distance. Initialize to something big. + T minDist = std::numeric_limits::infinity(); + + // Create temporary storage and and priority queue (our stack). + using NodeAndDist = std::pair; - // Sort the child nodes and put them on the stack. On the next iteration - // we do the closest node first. This sorting is critical to the - // performance of the BVH. - std::sort( - children.begin(), children.end(), - [](const std::pair& node1, const std::pair& node2) -> bool { - return node1.second > node2.second; - }); - - // Push onto stack if the BV is closer than minDist. - for (const auto& child : children) { - q.push(child); + std::array children; + std::stack q; + + // Initialize the stack with the root node. + q.emplace(0, m_linearNodes[0]->getDistanceToBoundingVolume(a_point)); + + // Stack loop -- always investigate the one at the top. + while (!(q.empty())) { + + // Pop the top node off the stack. + const auto& curNode = (q.top()).first; + const auto& bvDist = (q.top()).second; + + q.pop(); + + // See if we really need to process this node. We only need to do it if its + // BV is closer than the shortest distance we've found so far. Otherwise we + // are guaranteed that the distance to the primitives is larger than the + // shortest distance we've found so far. + if (bvDist <= std::abs(minDist)) { + + // If it's a leaf node, update the shortest distance so far. + if (m_linearNodes[curNode]->isLeaf()) { + const T primDist = m_linearNodes[curNode]->getDistanceToPrimitives(a_point, m_primitives); + + if (std::abs(primDist) < std::abs(minDist)) { + minDist = primDist; + } + } + else { + // Compute child indices and their BVH distance to a_point. + for (size_t k = 0; k < K; k++) { + const size_t& curOff = m_linearNodes[curNode]->getChildOffsets()[k]; + const T distanceToBV = m_linearNodes[curOff]->getDistanceToBoundingVolume(a_point); + + children[k] = std::make_pair(curOff, distanceToBV); + } + + // Sort the child nodes and put them on the stack. On the next iteration + // we do the closest node first. This sorting is critical to the + // performance of the BVH. + std::sort(children.begin(), + children.end(), + [](const std::pair& node1, const std::pair& node2) -> bool { + return node1.second > node2.second; + }); + + // Push onto stack if the BV is closer than minDist. + for (const auto& child : children) { + q.push(child); + } } } } - } - return minDist; -} + return minDist; + } } // namespace BVH #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_BoundingVolumes.hpp b/Source/EBGeometry_BoundingVolumes.hpp index da6f2417..07bc285d 100644 --- a/Source/EBGeometry_BoundingVolumes.hpp +++ b/Source/EBGeometry_BoundingVolumes.hpp @@ -26,359 +26,359 @@ */ namespace BoundingVolumes { -/*! - @brief Class which encloses a set of points using a bounding sphere. - @details The template parameter T is the floating-point precision which is - used. -*/ -template -class BoundingSphereT -{ -public: /*! - @brief Typename for possible algorithms that support the computation of a - bounding sphere for a set of 3D points. + @brief Class which encloses a set of points using a bounding sphere. + @details The template parameter T is the floating-point precision which is + used. */ - enum class BoundingVolumeAlgorithm + template + class BoundingSphereT { - Ritter, + public: + /*! + @brief Typename for possible algorithms that support the computation of a + bounding sphere for a set of 3D points. + */ + enum class BoundingVolumeAlgorithm + { + Ritter, + }; + + /*! + @brief Alias to cut down on typing + */ + using Vec3 = Vec3T; + + /*! + @brief Default constructor. Leaves object in undefined state. + */ + BoundingSphereT(); + + /*! + @brief Full constructor. Sets the center and radius of the bounding sphere. + @param[in] a_center Bounding sphere center + @param[in] a_radius Bounding sphere radius + */ + BoundingSphereT(const Vec3T& a_center, const T& a_radius); + + /*! + @brief Full constructor. Constructs a bounding sphere that encloses all the + other bounding spheres + @param[in] a_otherSpheres Other bounding spheres. + */ + BoundingSphereT(const std::vector>& a_otherSpheres); + + /*! + @brief Copy constructor. Sets the center and radius from the other sphere. + @param[in] a_other Other sphere + */ + BoundingSphereT(const BoundingSphereT& a_other); + + /*! + @brief Template constructor which takes a set of 3D points (mixed precision + allowed). + @details This computes the bounding sphere using the supplied algorithm. + @param[in] a_points Set of 3D points + @param[in] a_alg Bounding sphere algorithm. + @note This calls the define(...) function. + */ + template + BoundingSphereT(const std::vector>& a_points, + const BoundingVolumeAlgorithm& a_alg = BoundingVolumeAlgorithm::Ritter); + + /*! + @brief Destructor (does nothing). + */ + virtual ~BoundingSphereT(); + + /*! + @brief Copy assignment operator + @param[in] a_other Other sphere + */ + BoundingSphereT& + operator=(const BoundingSphereT& a_other) = default; + + /*! + @brief Template define function which takes a set of 3D points (mixed + precision allowed). + @details This computes the bounding sphere using the supplied algorithm. + @param[in] a_points Set of 3D points + @param[in] a_alg Bounding sphere algorithm. + */ + template + inline void + define(const std::vector>& a_points, const BoundingVolumeAlgorithm& a_alg) noexcept; + + /*! + @brief Check if this bounding sphere intersect another bounding sphere + @param[in] a_other Other bounding sphere. + @return True if the two sphere intersect. + */ + inline bool + intersects(const BoundingSphereT& a_other) const noexcept; + + /*! + @brief Get modifiable radius for this sphere + */ + inline T& + getRadius() noexcept; + + /*! + @brief Get immutable radius for this sphere + */ + inline const T& + getRadius() const noexcept; + + /*! + @brief Get modifiable center for this sphere + */ + inline Vec3& + getCentroid() noexcept; + + /*! + @brief Get immutable center for this sphere + */ + inline const Vec3& + getCentroid() const noexcept; + + /*! + @brief Compute the overlapping volume between this bounding sphere and + another + @param[in] a_other Other bounding sphere + @return The overlapping volume, computing using standard expressions. + */ + inline T + getOverlappingVolume(const BoundingSphereT& a_other) const noexcept; + + /*! + @brief Get the distance to this bounding sphere (points inside the sphere + have a zero distance) + @param[in] a_x0 3D point + @return Returns the distance to the sphere (a point inside has a zero + distance) + */ + inline T + getDistance(const Vec3& a_x0) const noexcept; + + /*! + @brief Get the sphere volume + @return Sphere volume + */ + inline T + getVolume() const noexcept; + + /*! + @brief Get the sphere area + @return Sphere area. + */ + inline T + getArea() const noexcept; + + protected: + /*! + @brief Sphere radius + */ + T m_radius; + + /*! + @brief Sphere center + */ + Vec3 m_center; + + /*! + @brief Template function which computes the bounding sphere for a set of + points (mixed precision allowed) using Ritter's algorithm + */ + template + inline void + buildRitter(const std::vector>& a_points) noexcept; }; /*! - @brief Alias to cut down on typing - */ - using Vec3 = Vec3T; - - /*! - @brief Default constructor. Leaves object in undefined state. - */ - BoundingSphereT(); - - /*! - @brief Full constructor. Sets the center and radius of the bounding sphere. - @param[in] a_center Bounding sphere center - @param[in] a_radius Bounding sphere radius - */ - BoundingSphereT(const Vec3T& a_center, const T& a_radius); - - /*! - @brief Full constructor. Constructs a bounding sphere that encloses all the - other bounding spheres - @param[in] a_otherSpheres Other bounding spheres. - */ - BoundingSphereT(const std::vector>& a_otherSpheres); - - /*! - @brief Copy constructor. Sets the center and radius from the other sphere. - @param[in] a_other Other sphere - */ - BoundingSphereT(const BoundingSphereT& a_other); - - /*! - @brief Template constructor which takes a set of 3D points (mixed precision - allowed). - @details This computes the bounding sphere using the supplied algorithm. - @param[in] a_points Set of 3D points - @param[in] a_alg Bounding sphere algorithm. - @note This calls the define(...) function. + @brief Axis-aligned bounding box as bounding volume + @details This class represents a Cartesian box that encloses a set of 3D + points. + @note The template parameter T is the precision. */ - template - BoundingSphereT( - const std::vector>& a_points, const BoundingVolumeAlgorithm& a_alg = BoundingVolumeAlgorithm::Ritter); - - /*! - @brief Destructor (does nothing). - */ - virtual ~BoundingSphereT(); - - /*! - @brief Copy assignment operator - @param[in] a_other Other sphere - */ - BoundingSphereT& - operator=(const BoundingSphereT& a_other) = default; - - /*! - @brief Template define function which takes a set of 3D points (mixed - precision allowed). - @details This computes the bounding sphere using the supplied algorithm. - @param[in] a_points Set of 3D points - @param[in] a_alg Bounding sphere algorithm. - */ - template - inline void - define(const std::vector>& a_points, const BoundingVolumeAlgorithm& a_alg) noexcept; - - /*! - @brief Check if this bounding sphere intersect another bounding sphere - @param[in] a_other Other bounding sphere. - @return True if the two sphere intersect. - */ - inline bool - intersects(const BoundingSphereT& a_other) const noexcept; - - /*! - @brief Get modifiable radius for this sphere - */ - inline T& - getRadius() noexcept; - - /*! - @brief Get immutable radius for this sphere - */ - inline const T& - getRadius() const noexcept; - - /*! - @brief Get modifiable center for this sphere - */ - inline Vec3& - getCentroid() noexcept; - - /*! - @brief Get immutable center for this sphere - */ - inline const Vec3& - getCentroid() const noexcept; - - /*! - @brief Compute the overlapping volume between this bounding sphere and - another - @param[in] a_other Other bounding sphere - @return The overlapping volume, computing using standard expressions. - */ - inline T - getOverlappingVolume(const BoundingSphereT& a_other) const noexcept; - - /*! - @brief Get the distance to this bounding sphere (points inside the sphere - have a zero distance) - @param[in] a_x0 3D point - @return Returns the distance to the sphere (a point inside has a zero - distance) - */ - inline T - getDistance(const Vec3& a_x0) const noexcept; - - /*! - @brief Get the sphere volume - @return Sphere volume - */ - inline T - getVolume() const noexcept; - - /*! - @brief Get the sphere area - @return Sphere area. - */ - inline T - getArea() const noexcept; - -protected: - /*! - @brief Sphere radius - */ - T m_radius; - - /*! - @brief Sphere center - */ - Vec3 m_center; - - /*! - @brief Template function which computes the bounding sphere for a set of - points (mixed precision allowed) using Ritter's algorithm - */ - template - inline void - buildRitter(const std::vector>& a_points) noexcept; -}; - -/*! - @brief Axis-aligned bounding box as bounding volume - @details This class represents a Cartesian box that encloses a set of 3D - points. - @note The template parameter T is the precision. -*/ -template -class AABBT -{ -public: - /*! - @brief Alias which cuts down on typing - */ - using Vec3 = Vec3T; - - /*! - @brief Default constructor (does nothing) - */ - AABBT(); - - /*! - @brief Full constructor taking the low/high corners of the bounding box - @param[in] a_lo Low corner - @param[in] a_hi High - */ - AABBT(const Vec3T& a_lo, const Vec3T& a_hi); - - /*! - @brief Copy constructor of another bounding box - @param[in] a_other Other bounding box - */ - AABBT(const AABBT& a_other); - - /*! - @brief Constructor which creates an AABB which encloses a set of other - AABBs. - @param[in] a_others Other bounding boxes - */ - AABBT(const std::vector>& a_others); - - /*! - @brief Template constructor (since mixed precision allowed) which creates an - AABB that encloses a set of 3D points - @param[in] a_points Set of 3D points - @note Calls the define function - */ - template - AABBT(const std::vector>& a_points); - - /*! - @brief Destructor (does nothing) - */ - virtual ~AABBT(); - - /*! - @brief Copy assignment - @param[in] a_other Other bounding box - */ - AABBT& - operator=(const AABBT& a_other) = default; - - /*! - @brief Define function (since mixed precision allowed) which sets this AABB - such that it encloses a set of 3D points - @param[in] a_points Set of 3D points - */ - template - inline void - define(const std::vector>& a_points) noexcept; - - /*! - @brief Check if this AABB intersects another AABB - @param[in] a_other The other AABB - @return True if they intersect and false otherwise. - */ - inline bool - intersects(const AABBT& a_other) const noexcept; - - /*! - @brief Get the modifiable lower-left corner of the AABB - */ - inline Vec3T& - getLowCorner() noexcept; - - /*! - @brief Get the immutable lower-left corner of the AABB - */ - inline const Vec3T& - getLowCorner() const noexcept; - - /*! - @brief Get the modifiable upper-right corner of the AABB - */ - inline Vec3T& - getHighCorner() noexcept; - - /*! - @brief Get the immutable upper-right corner of the AABB - */ - inline const Vec3T& - getHighCorner() const noexcept; - - /*! - @brief Get bounding volume centroid. - */ - inline Vec3 - getCentroid() const noexcept; - - /*! - @brief Compute the overlapping volume between this AABB and another AABB. - @param[in] a_other The other AABB - @return Returns overlapping volume - */ - inline T - getOverlappingVolume(const AABBT& a_other) const noexcept; - - /*! - @brief Get the distance to this AABB (points inside the bounding box have a - zero distance) - @param[in] a_x0 3D point - @return Returns the distance to the bounding box (a point inside has a zero - distance) - */ - inline T - getDistance(const Vec3& a_x0) const noexcept; + template + class AABBT + { + public: + /*! + @brief Alias which cuts down on typing + */ + using Vec3 = Vec3T; + + /*! + @brief Default constructor (does nothing) + */ + AABBT(); + + /*! + @brief Full constructor taking the low/high corners of the bounding box + @param[in] a_lo Low corner + @param[in] a_hi High + */ + AABBT(const Vec3T& a_lo, const Vec3T& a_hi); + + /*! + @brief Copy constructor of another bounding box + @param[in] a_other Other bounding box + */ + AABBT(const AABBT& a_other); + + /*! + @brief Constructor which creates an AABB which encloses a set of other + AABBs. + @param[in] a_others Other bounding boxes + */ + AABBT(const std::vector>& a_others); + + /*! + @brief Template constructor (since mixed precision allowed) which creates an + AABB that encloses a set of 3D points + @param[in] a_points Set of 3D points + @note Calls the define function + */ + template + AABBT(const std::vector>& a_points); + + /*! + @brief Destructor (does nothing) + */ + virtual ~AABBT(); + + /*! + @brief Copy assignment + @param[in] a_other Other bounding box + */ + AABBT& + operator=(const AABBT& a_other) = default; + + /*! + @brief Define function (since mixed precision allowed) which sets this AABB + such that it encloses a set of 3D points + @param[in] a_points Set of 3D points + */ + template + inline void + define(const std::vector>& a_points) noexcept; + + /*! + @brief Check if this AABB intersects another AABB + @param[in] a_other The other AABB + @return True if they intersect and false otherwise. + */ + inline bool + intersects(const AABBT& a_other) const noexcept; + + /*! + @brief Get the modifiable lower-left corner of the AABB + */ + inline Vec3T& + getLowCorner() noexcept; + + /*! + @brief Get the immutable lower-left corner of the AABB + */ + inline const Vec3T& + getLowCorner() const noexcept; + + /*! + @brief Get the modifiable upper-right corner of the AABB + */ + inline Vec3T& + getHighCorner() noexcept; + + /*! + @brief Get the immutable upper-right corner of the AABB + */ + inline const Vec3T& + getHighCorner() const noexcept; + + /*! + @brief Get bounding volume centroid. + */ + inline Vec3 + getCentroid() const noexcept; + + /*! + @brief Compute the overlapping volume between this AABB and another AABB. + @param[in] a_other The other AABB + @return Returns overlapping volume + */ + inline T + getOverlappingVolume(const AABBT& a_other) const noexcept; + + /*! + @brief Get the distance to this AABB (points inside the bounding box have a + zero distance) + @param[in] a_x0 3D point + @return Returns the distance to the bounding box (a point inside has a zero + distance) + */ + inline T + getDistance(const Vec3& a_x0) const noexcept; + + /*! + @brief Compute the bounding box volume + */ + inline T + getVolume() const noexcept; + + /*! + @brief Compute the bounding box area + */ + inline T + getArea() const noexcept; + + protected: + /*! + @brief Lower-left corner of bounding box + */ + Vec3 m_loCorner; + + /*! + @brief Upper-right corner of bounding box + */ + Vec3 m_hiCorner; + }; /*! - @brief Compute the bounding box volume + @brief Intersection method for testing if two bounding spheres overlap + @param[in] a_u One bounding sphere + @param[in] a_v The other bounding sphere */ - inline T - getVolume() const noexcept; + template + bool + intersects(const BoundingSphereT& a_u, const BoundingSphereT& a_v) noexcept; /*! - @brief Compute the bounding box area + @brief Intersection method for testing if two bounding boxes overlap + @param[in] a_u One bounding box + @param[in] a_v The other bounding box */ - inline T - getArea() const noexcept; + template + bool + intersects(const AABBT& a_u, const AABBT& a_v) noexcept; -protected: /*! - @brief Lower-left corner of bounding box + @brief Compute the overlapping volume between two bounding spheres + @param[in] a_u One bounding sphere + @param[in] a_v The other bounding sphere */ - Vec3 m_loCorner; + template + T + getOverlappingVolume(const BoundingSphereT& a_u, const BoundingSphereT& a_v) noexcept; /*! - @brief Upper-right corner of bounding box + @brief Compute the overlapping volume between two bounding boxes + @param[in] a_u One bounding box + @param[in] a_v The other bounding box */ - Vec3 m_hiCorner; -}; - -/*! - @brief Intersection method for testing if two bounding spheres overlap - @param[in] a_u One bounding sphere - @param[in] a_v The other bounding sphere -*/ -template -bool -intersects(const BoundingSphereT& a_u, const BoundingSphereT& a_v) noexcept; - -/*! - @brief Intersection method for testing if two bounding boxes overlap - @param[in] a_u One bounding box - @param[in] a_v The other bounding box -*/ -template -bool -intersects(const AABBT& a_u, const AABBT& a_v) noexcept; - -/*! - @brief Compute the overlapping volume between two bounding spheres - @param[in] a_u One bounding sphere - @param[in] a_v The other bounding sphere -*/ -template -T -getOverlappingVolume(const BoundingSphereT& a_u, const BoundingSphereT& a_v) noexcept; - -/*! - @brief Compute the overlapping volume between two bounding boxes - @param[in] a_u One bounding box - @param[in] a_v The other bounding box -*/ -template -T -getOverlappingVolume(const AABBT& a_u, const AABBT& a_v) noexcept; + template + T + getOverlappingVolume(const AABBT& a_u, const AABBT& a_v) noexcept; } // namespace BoundingVolumes #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_BoundingVolumesImplem.hpp b/Source/EBGeometry_BoundingVolumesImplem.hpp index 8253bd0d..b5ce0a73 100644 --- a/Source/EBGeometry_BoundingVolumesImplem.hpp +++ b/Source/EBGeometry_BoundingVolumesImplem.hpp @@ -21,429 +21,427 @@ namespace BoundingVolumes { -template -inline BoundingSphereT::BoundingSphereT() -{ - m_radius = 0.0; - m_center = Vec3::zero(); -} - -template -inline BoundingSphereT::BoundingSphereT(const Vec3T& a_center, const T& a_radius) -{ - m_center = a_center; - m_radius = a_radius; -} - -template -BoundingSphereT::BoundingSphereT(const BoundingSphereT& a_other) -{ - m_radius = a_other.m_radius; - m_center = a_other.m_center; -} - -template -BoundingSphereT::BoundingSphereT(const std::vector>& a_otherSpheres) -{ - - // TLDR: Spheres enclosing other spheres is a difficult problem, but a sphere - // enclosing a set of points is simpler. For each - // input sphere we create a set of points representing the lo/hicorners - // of an axis-aligned bounding box that encloses the sphere. We then - // compute the bounding sphere from this set of points. - std::vector> points; - for (const auto& sphere : a_otherSpheres) { - const T& radius = sphere.getRadius(); - const Vec3T& center = sphere.getCentroid(); - - points.emplace_back(center + radius * Vec3T::one()); - points.emplace_back(center - radius * Vec3T::one()); - } - - this->define(points, BoundingSphereT::BoundingVolumeAlgorithm::Ritter); -} - -template -template -BoundingSphereT::BoundingSphereT(const std::vector>& a_points, const BoundingVolumeAlgorithm& a_algorithm) -{ - this->define(a_points, a_algorithm); -} - -template -BoundingSphereT::~BoundingSphereT() -{ -} - -template -template -inline void -BoundingSphereT::define(const std::vector>& a_points, const BoundingVolumeAlgorithm& a_algorithm) noexcept -{ - switch (a_algorithm) { - case BoundingVolumeAlgorithm::Ritter: { - this->buildRitter(a_points); - - break; - } - default: { - std::cerr << "BoundingSphereT::define - unsupported algorithm requested\n"; - } - } -} - -template -inline bool -BoundingSphereT::intersects(const BoundingSphereT& a_other) const noexcept -{ - const Vec3 deltaV = m_center - a_other.getCentroid(); - const T sumR = m_radius + a_other.getRadius(); - - return deltaV.dot(deltaV) < sumR * sumR; -} - -template -inline T& -BoundingSphereT::getRadius() noexcept -{ - return (m_radius); -} - -template -inline const T& -BoundingSphereT::getRadius() const noexcept -{ - return (m_radius); -} - -template -inline Vec3T& -BoundingSphereT::getCentroid() noexcept -{ - return (m_center); -} - -template -inline const Vec3T& -BoundingSphereT::getCentroid() const noexcept -{ - return (m_center); -} - -template -template -inline void -BoundingSphereT::buildRitter(const std::vector>& a_points) noexcept -{ - m_radius = 0.0; - m_center = Vec3::zero(); - - constexpr T half = 0.5; - - constexpr size_t DIM = 3; - - // INITIAL PASS - std::vector min_coord(DIM, a_points[0]); // [0] = Minimum x, [1] = Minimum y, [2] = Minimum z - std::vector max_coord(DIM, a_points[0]); - - for (size_t i = 1; i < a_points.size(); i++) { - for (size_t dir = 0; dir < DIM; dir++) { - Vec3& min = min_coord[dir]; - Vec3& max = max_coord[dir]; + template + inline BoundingSphereT::BoundingSphereT() + { + m_radius = 0.0; + m_center = Vec3::zero(); + } + + template + inline BoundingSphereT::BoundingSphereT(const Vec3T& a_center, const T& a_radius) + { + m_center = a_center; + m_radius = a_radius; + } + + template + BoundingSphereT::BoundingSphereT(const BoundingSphereT& a_other) + { + m_radius = a_other.m_radius; + m_center = a_other.m_center; + } + + template + BoundingSphereT::BoundingSphereT(const std::vector>& a_otherSpheres) + { + + // TLDR: Spheres enclosing other spheres is a difficult problem, but a sphere + // enclosing a set of points is simpler. For each + // input sphere we create a set of points representing the lo/hicorners + // of an axis-aligned bounding box that encloses the sphere. We then + // compute the bounding sphere from this set of points. + std::vector> points; + for (const auto& sphere : a_otherSpheres) { + const T& radius = sphere.getRadius(); + const Vec3T& center = sphere.getCentroid(); + + points.emplace_back(center + radius * Vec3T::one()); + points.emplace_back(center - radius * Vec3T::one()); + } + + this->define(points, BoundingSphereT::BoundingVolumeAlgorithm::Ritter); + } + + template + template + BoundingSphereT::BoundingSphereT(const std::vector>& a_points, const BoundingVolumeAlgorithm& a_algorithm) + { + this->define(a_points, a_algorithm); + } + + template + BoundingSphereT::~BoundingSphereT() + {} + + template + template + inline void + BoundingSphereT::define(const std::vector>& a_points, const BoundingVolumeAlgorithm& a_algorithm) noexcept + { + switch (a_algorithm) { + case BoundingVolumeAlgorithm::Ritter: { + this->buildRitter(a_points); - if (a_points[i][dir] < min[dir]) { - min = a_points[i]; + break; + } + default: { + std::cerr << "BoundingSphereT::define - unsupported algorithm requested\n"; + } + } + } + + template + inline bool + BoundingSphereT::intersects(const BoundingSphereT& a_other) const noexcept + { + const Vec3 deltaV = m_center - a_other.getCentroid(); + const T sumR = m_radius + a_other.getRadius(); + + return deltaV.dot(deltaV) < sumR * sumR; + } + + template + inline T& + BoundingSphereT::getRadius() noexcept + { + return (m_radius); + } + + template + inline const T& + BoundingSphereT::getRadius() const noexcept + { + return (m_radius); + } + + template + inline Vec3T& + BoundingSphereT::getCentroid() noexcept + { + return (m_center); + } + + template + inline const Vec3T& + BoundingSphereT::getCentroid() const noexcept + { + return (m_center); + } + + template + template + inline void + BoundingSphereT::buildRitter(const std::vector>& a_points) noexcept + { + m_radius = 0.0; + m_center = Vec3::zero(); + + constexpr T half = 0.5; + + constexpr size_t DIM = 3; + + // INITIAL PASS + std::vector min_coord(DIM, a_points[0]); // [0] = Minimum x, [1] = Minimum y, [2] = Minimum z + std::vector max_coord(DIM, a_points[0]); + + for (size_t i = 1; i < a_points.size(); i++) { + for (size_t dir = 0; dir < DIM; dir++) { + Vec3& min = min_coord[dir]; + Vec3& max = max_coord[dir]; + + if (a_points[i][dir] < min[dir]) { + min = a_points[i]; + } + if (a_points[i][dir] > max[dir]) { + max = a_points[i]; + } } - if (a_points[i][dir] > max[dir]) { - max = a_points[i]; + } + + T dist = -1; + Vec3 v, p1, p2; + for (size_t dir = 0; dir < DIM; dir++) { + const T len = (max_coord[dir] - min_coord[dir]).length(); + if (len > dist) { + dist = len; + p1 = min_coord[dir]; + p2 = max_coord[dir]; } } + + // m_center = half*(p1+p2); + m_center = (p1 + p2) * half; + m_radius = half * (p2 - p1).length(); + + // SECOND PASS + for (size_t i = 0; i < a_points.size(); i++) { + dist = (a_points[i] - m_center).length() - m_radius; + if (dist > 0.0) { // Point lies outside + v = a_points[i] - m_center; + p1 = a_points[i]; + p2 = m_center - m_radius * v / v.length(); + + m_center = half * (p2 + p1); + m_radius = half * (p2 - p1).length(); + } + } + + // Ritter algorithm is very coarse and does not give an exact result anyways. + // Grow the dimension for safety. + m_radius *= (1.0 + 1E-2); + } + + template + inline T + BoundingSphereT::getOverlappingVolume(const BoundingSphereT& a_other) const noexcept + { + constexpr T zero = 0.0; + + T retval = zero; + + if (this->intersects(a_other)) { + const auto& r1 = m_radius; + const auto& r2 = a_other.getRadius(); + + const auto d = (m_center - a_other.getCentroid()).length(); + + retval = + M_PI / (12. * d) * (r1 + r2 - d) * (r1 + r2 - d) * (d * d + 2 * d * (r1 + r2) - 3 * (r1 - r2) * (r1 - r2)); + } + + return retval; + } + + template + inline T + BoundingSphereT::getDistance(const Vec3& a_x0) const noexcept + { + constexpr T zero = 0.0; + + return std::max(zero, (a_x0 - m_center).length() - m_radius); + } + + template + inline T + BoundingSphereT::getVolume() const noexcept + { + return 4. * M_PI * m_radius * m_radius * m_radius / 3.0; + } + + template + inline T + BoundingSphereT::getArea() const noexcept + { + return T(4. * M_PI * m_radius * m_radius); + } + + template + AABBT::AABBT() + { + m_loCorner = Vec3::zero(); + m_hiCorner = Vec3::zero(); + } + + template + AABBT::AABBT(const Vec3T& a_lo, const Vec3T& a_hi) + { + m_loCorner = a_lo; + m_hiCorner = a_hi; + } + + template + AABBT::AABBT(const AABBT& a_other) + { + m_loCorner = a_other.m_loCorner; + m_hiCorner = a_other.m_hiCorner; + } + + template + AABBT::AABBT(const std::vector>& a_others) + { + m_loCorner = a_others.front().getLowCorner(); + m_hiCorner = a_others.front().getHighCorner(); + + for (const auto& other : a_others) { + m_loCorner = min(m_loCorner, other.getLowCorner()); + m_hiCorner = max(m_hiCorner, other.getHighCorner()); + } } - T dist = -1; - Vec3 v, p1, p2; - for (size_t dir = 0; dir < DIM; dir++) { - const T len = (max_coord[dir] - min_coord[dir]).length(); - if (len > dist) { - dist = len; - p1 = min_coord[dir]; - p2 = max_coord[dir]; + template + template + AABBT::AABBT(const std::vector>& a_points) + { + this->define(a_points); + } + + template + AABBT::~AABBT() + {} + + template + template + inline void + AABBT::define(const std::vector>& a_points) noexcept + { + m_loCorner = a_points.front(); + m_hiCorner = a_points.front(); + + for (const auto& p : a_points) { + m_loCorner = min(m_loCorner, p); + m_hiCorner = max(m_hiCorner, p); } } - // m_center = half*(p1+p2); - m_center = (p1 + p2) * half; - m_radius = half * (p2 - p1).length(); + template + inline bool + AABBT::intersects(const AABBT& a_other) const noexcept + { + const Vec3& otherLo = a_other.getLowCorner(); + const Vec3& otherHi = a_other.getHighCorner(); + + return (m_loCorner[0] < otherHi[0] && m_hiCorner[0] > otherLo[0]) && + (m_loCorner[1] < otherHi[1] && m_hiCorner[1] > otherLo[1]) && + (m_loCorner[2] < otherHi[2] && m_hiCorner[2] > otherLo[2]); + } + + template + inline Vec3T& + AABBT::getLowCorner() noexcept + { + return (m_loCorner); + } + + template + inline const Vec3T& + AABBT::getLowCorner() const noexcept + { + return (m_loCorner); + } + + template + inline Vec3T& + AABBT::getHighCorner() noexcept + { + return (m_hiCorner); + } + + template + inline const Vec3T& + AABBT::getHighCorner() const noexcept + { + return (m_hiCorner); + } + + template + inline Vec3T + AABBT::getCentroid() const noexcept + { + constexpr T half = T(0.5); + + return half * (m_loCorner + m_hiCorner); + } + + template + inline T + AABBT::getOverlappingVolume(const AABBT& a_other) const noexcept + { + constexpr T zero = 0.0; - // SECOND PASS - for (size_t i = 0; i < a_points.size(); i++) { - dist = (a_points[i] - m_center).length() - m_radius; - if (dist > 0.0) { // Point lies outside - v = a_points[i] - m_center; - p1 = a_points[i]; - p2 = m_center - m_radius * v / v.length(); + T ret = 1.0; - m_center = half * (p2 + p1); - m_radius = half * (p2 - p1).length(); + for (size_t dir = 0; dir < 3; dir++) { + const auto xL = m_loCorner[dir]; + const auto xH = m_hiCorner[dir]; + + const auto yL = a_other.m_loCorner[dir]; + const auto yH = a_other.m_hiCorner[dir]; + + const auto delta = std::max(zero, std::min(xH, yH) - std::max(xL, yL)); + + ret *= delta; + } + + return ret; + } + + template + inline T + AABBT::getDistance(const Vec3& a_point) const noexcept + { + constexpr T zero = 0.0; + + const Vec3 delta = Vec3(std::max(m_loCorner[0] - a_point[0], a_point[0] - m_hiCorner[0]), + std::max(m_loCorner[1] - a_point[1], a_point[1] - m_hiCorner[1]), + std::max(m_loCorner[2] - a_point[2], a_point[2] - m_hiCorner[2])); + + const T retval = std::max(zero, max(Vec3::zero(), delta).length()); + + return retval; + } + + template + inline T + AABBT::getVolume() const noexcept + { + const auto delta = m_hiCorner - m_loCorner; + + T ret = 1.0; + for (size_t dir = 0; dir < 3; dir++) { + ret *= delta[dir]; } + + return ret; } - // Ritter algorithm is very coarse and does not give an exact result anyways. - // Grow the dimension for safety. - m_radius *= (1.0 + 1E-2); -} - -template -inline T -BoundingSphereT::getOverlappingVolume(const BoundingSphereT& a_other) const noexcept -{ - constexpr T zero = 0.0; - - T retval = zero; - - if (this->intersects(a_other)) { - const auto& r1 = m_radius; - const auto& r2 = a_other.getRadius(); - - const auto d = (m_center - a_other.getCentroid()).length(); - - retval = M_PI / (12. * d) * (r1 + r2 - d) * (r1 + r2 - d) * (d * d + 2 * d * (r1 + r2) - 3 * (r1 - r2) * (r1 - r2)); - } - - return retval; -} - -template -inline T -BoundingSphereT::getDistance(const Vec3& a_x0) const noexcept -{ - constexpr T zero = 0.0; - - return std::max(zero, (a_x0 - m_center).length() - m_radius); -} - -template -inline T -BoundingSphereT::getVolume() const noexcept -{ - return 4. * M_PI * m_radius * m_radius * m_radius / 3.0; -} - -template -inline T -BoundingSphereT::getArea() const noexcept -{ - return T(4. * M_PI * m_radius * m_radius); -} - -template -AABBT::AABBT() -{ - m_loCorner = Vec3::zero(); - m_hiCorner = Vec3::zero(); -} - -template -AABBT::AABBT(const Vec3T& a_lo, const Vec3T& a_hi) -{ - m_loCorner = a_lo; - m_hiCorner = a_hi; -} - -template -AABBT::AABBT(const AABBT& a_other) -{ - m_loCorner = a_other.m_loCorner; - m_hiCorner = a_other.m_hiCorner; -} - -template -AABBT::AABBT(const std::vector>& a_others) -{ - m_loCorner = a_others.front().getLowCorner(); - m_hiCorner = a_others.front().getHighCorner(); - - for (const auto& other : a_others) { - m_loCorner = min(m_loCorner, other.getLowCorner()); - m_hiCorner = max(m_hiCorner, other.getHighCorner()); - } -} - -template -template -AABBT::AABBT(const std::vector>& a_points) -{ - this->define(a_points); -} - -template -AABBT::~AABBT() -{ -} - -template -template -inline void -AABBT::define(const std::vector>& a_points) noexcept -{ - m_loCorner = a_points.front(); - m_hiCorner = a_points.front(); - - for (const auto& p : a_points) { - m_loCorner = min(m_loCorner, p); - m_hiCorner = max(m_hiCorner, p); - } -} - -template -inline bool -AABBT::intersects(const AABBT& a_other) const noexcept -{ - const Vec3& otherLo = a_other.getLowCorner(); - const Vec3& otherHi = a_other.getHighCorner(); - - return (m_loCorner[0] < otherHi[0] && m_hiCorner[0] > otherLo[0]) && - (m_loCorner[1] < otherHi[1] && m_hiCorner[1] > otherLo[1]) && - (m_loCorner[2] < otherHi[2] && m_hiCorner[2] > otherLo[2]); -} - -template -inline Vec3T& -AABBT::getLowCorner() noexcept -{ - return (m_loCorner); -} - -template -inline const Vec3T& -AABBT::getLowCorner() const noexcept -{ - return (m_loCorner); -} - -template -inline Vec3T& -AABBT::getHighCorner() noexcept -{ - return (m_hiCorner); -} - -template -inline const Vec3T& -AABBT::getHighCorner() const noexcept -{ - return (m_hiCorner); -} - -template -inline Vec3T -AABBT::getCentroid() const noexcept -{ - constexpr T half = T(0.5); - - return half * (m_loCorner + m_hiCorner); -} - -template -inline T -AABBT::getOverlappingVolume(const AABBT& a_other) const noexcept -{ - constexpr T zero = 0.0; - - T ret = 1.0; - - for (size_t dir = 0; dir < 3; dir++) { - const auto xL = m_loCorner[dir]; - const auto xH = m_hiCorner[dir]; - - const auto yL = a_other.m_loCorner[dir]; - const auto yH = a_other.m_hiCorner[dir]; - - const auto delta = std::max(zero, std::min(xH, yH) - std::max(xL, yL)); - - ret *= delta; - } - - return ret; -} - -template -inline T -AABBT::getDistance(const Vec3& a_point) const noexcept -{ - constexpr T zero = 0.0; - - const Vec3 delta = Vec3( - std::max(m_loCorner[0] - a_point[0], a_point[0] - m_hiCorner[0]), - std::max(m_loCorner[1] - a_point[1], a_point[1] - m_hiCorner[1]), - std::max(m_loCorner[2] - a_point[2], a_point[2] - m_hiCorner[2])); - - const T retval = std::max(zero, max(Vec3::zero(), delta).length()); - - return retval; -} - -template -inline T -AABBT::getVolume() const noexcept -{ - const auto delta = m_hiCorner - m_loCorner; - - T ret = 1.0; - for (size_t dir = 0; dir < 3; dir++) { - ret *= delta[dir]; - } - - return ret; -} - -template -inline T -AABBT::getArea() const noexcept -{ - constexpr size_t DIM = 3; - - T ret = 0.0; - - const auto delta = m_hiCorner - m_loCorner; - - for (size_t dir = 0; dir < DIM; dir++) { - const size_t otherDir1 = (dir + 1) % DIM; - const size_t otherDir2 = (dir + 2) % DIM; - - ret += 2.0 * delta[otherDir1] * delta[otherDir2]; - } + template + inline T + AABBT::getArea() const noexcept + { + constexpr size_t DIM = 3; + + T ret = 0.0; - return ret; -} - -template -bool -intersects(const BoundingSphereT& u, const BoundingSphereT& v) noexcept -{ - return u.intersects(v); -} + const auto delta = m_hiCorner - m_loCorner; -template -bool -intersects(const AABBT& u, const AABBT& v) noexcept -{ - return u.intersects(v); -} + for (size_t dir = 0; dir < DIM; dir++) { + const size_t otherDir1 = (dir + 1) % DIM; + const size_t otherDir2 = (dir + 2) % DIM; -template -T -getOverlappingVolume(const BoundingSphereT& u, const BoundingSphereT& v) noexcept -{ - return u.getOverlappingVolume(v); -} + ret += 2.0 * delta[otherDir1] * delta[otherDir2]; + } -template -T -getOverlappingVolume(const AABBT& u, const AABBT& v) noexcept -{ - return u.getOverlappingVolume(v); -} + return ret; + } + + template + bool + intersects(const BoundingSphereT& u, const BoundingSphereT& v) noexcept + { + return u.intersects(v); + } + + template + bool + intersects(const AABBT& u, const AABBT& v) noexcept + { + return u.intersects(v); + } + + template + T + getOverlappingVolume(const BoundingSphereT& u, const BoundingSphereT& v) noexcept + { + return u.getOverlappingVolume(v); + } + + template + T + getOverlappingVolume(const AABBT& u, const AABBT& v) noexcept + { + return u.getOverlappingVolume(v); + } } // namespace BoundingVolumes #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_BVH.hpp b/Source/EBGeometry_DCEL_BVH.hpp index 63d93d33..60316d25 100644 --- a/Source/EBGeometry_DCEL_BVH.hpp +++ b/Source/EBGeometry_DCEL_BVH.hpp @@ -25,223 +25,223 @@ namespace DCEL { -/*! - @brief Alias for vector of primitives. -*/ -template -using PrimitiveList = std::vector>>; + /*! + @brief Alias for vector of primitives. + */ + template + using PrimitiveList = std::vector>>; + + /*! + @brief Bounding volume constructor for a DCEL face. + @details With BVHs and DCEL, the object to be bounded is the polygon face + (e.g., triangle). We assume that our BV constructor can enclose points, so we + return an object that encloses all the vertices of the polygon. + @param[in] a_primitive Primitive (facet) to be bounded. + @return Returns a bounding volume which encloses the input face. + */ + template + EBGeometry::BVH::BVConstructorT, BV> defaultBVConstructor = + [](const std::shared_ptr>& a_primitive) -> BV { + return BV(a_primitive->getAllVertexCoordinates()); + }; -/*! - @brief Bounding volume constructor for a DCEL face. - @details With BVHs and DCEL, the object to be bounded is the polygon face - (e.g., triangle). We assume that our BV constructor can enclose points, so we - return an object that encloses all the vertices of the polygon. - @param[in] a_primitive Primitive (facet) to be bounded. - @return Returns a bounding volume which encloses the input face. -*/ -template -EBGeometry::BVH::BVConstructorT, BV> defaultBVConstructor = - [](const std::shared_ptr>& a_primitive) -> BV { - return BV(a_primitive->getAllVertexCoordinates()); -}; + /*! + @brief Default stop function. This function terminates the division process if + a BVH node has only one primitive. + @details In this function, BVH::NodeT, BVH > is a BVH node. The + interpretation of the parameters are: T is the precision, FaceT is the + primitive type in the BVH tree, and BV is the bounding volume type. + @param[in] a_node Bounding volume hierarchy node. + @return Returns true if the bounding volume shouldn't be split more and false + otherwise. + */ + template + EBGeometry::BVH::StopFunctionT, BV, K> defaultStopFunction = + [](const BVH::NodeT, BV, K>& a_node) -> bool { + return (a_node.getPrimitives()).size() < K; + }; -/*! - @brief Default stop function. This function terminates the division process if - a BVH node has only one primitive. - @details In this function, BVH::NodeT, BVH > is a BVH node. The - interpretation of the parameters are: T is the precision, FaceT is the - primitive type in the BVH tree, and BV is the bounding volume type. - @param[in] a_node Bounding volume hierarchy node. - @return Returns true if the bounding volume shouldn't be split more and false - otherwise. -*/ -template -EBGeometry::BVH::StopFunctionT, BV, K> defaultStopFunction = - [](const BVH::NodeT, BV, K>& a_node) -> bool { - return (a_node.getPrimitives()).size() < K; -}; + /*! + @brief Function which checks that all chunks are valid (i.e., contain at least + one primitive + @param[in] a_chunks Chunks. + */ + template + auto validChunks = [](const std::array, K>& a_chunks) -> bool { + for (const auto& chunk : a_chunks) { + if (chunk.empty()) + return false; + } -/*! - @brief Function which checks that all chunks are valid (i.e., contain at least - one primitive - @param[in] a_chunks Chunks. -*/ -template -auto validChunks = [](const std::array, K>& a_chunks) -> bool { - for (const auto& chunk : a_chunks) { - if (chunk.empty()) - return false; - } + return true; + }; - return true; -}; + /*! + @brief Function for partitioning an input list into K almost-equal-sized + chunks + @param[in] a_primitives Primitives to be partitioned. + */ + template + auto equalCounts = [](const PrimitiveList& a_primitives) -> std::array, K> { + int length = a_primitives.size() / K; + int remain = a_primitives.size() % K; -/*! - @brief Function for partitioning an input list into K almost-equal-sized - chunks - @param[in] a_primitives Primitives to be partitioned. -*/ -template -auto equalCounts = [](const PrimitiveList& a_primitives) -> std::array, K> { - int length = a_primitives.size() / K; - int remain = a_primitives.size() % K; + int begin = 0; + int end = 0; - int begin = 0; - int end = 0; + std::array, K> chunks; - std::array, K> chunks; + for (size_t k = 0; k < K; k++) { + end += (remain > 0) ? length + 1 : length; + remain--; - for (size_t k = 0; k < K; k++) { - end += (remain > 0) ? length + 1 : length; - remain--; + chunks[k] = PrimitiveList(a_primitives.begin() + begin, a_primitives.begin() + end); - chunks[k] = PrimitiveList(a_primitives.begin() + begin, a_primitives.begin() + end); + begin = end; + } - begin = end; - } + return chunks; + }; - return chunks; -}; + /*! + @brief Partitioner function for subdividing into K sub-volumes with + approximately the same number of primitives. + @details This partitioner splits along one of the axis coordinates and sorts + the primitives along the centroid. + @param[in] a_primitives List of primitives to partition into sub-bounding + volumes + */ + template + EBGeometry::BVH::PartitionerT, BV, K> chunkPartitioner = + [](const PrimitiveList& a_primitives) -> std::array, K> { + Vec3T lo = Vec3T::max(); + Vec3T hi = -Vec3T::max(); + for (const auto& p : a_primitives) { + lo = min(lo, p->getCentroid()); + hi = max(hi, p->getCentroid()); + } -/*! - @brief Partitioner function for subdividing into K sub-volumes with - approximately the same number of primitives. - @details This partitioner splits along one of the axis coordinates and sorts - the primitives along the centroid. - @param[in] a_primitives List of primitives to partition into sub-bounding - volumes -*/ -template -EBGeometry::BVH::PartitionerT, BV, K> chunkPartitioner = - [](const PrimitiveList& a_primitives) -> std::array, K> { - Vec3T lo = Vec3T::max(); - Vec3T hi = -Vec3T::max(); - for (const auto& p : a_primitives) { - lo = min(lo, p->getCentroid()); - hi = max(hi, p->getCentroid()); - } - - const size_t splitDir = (hi - lo).maxDir(true); - - // Sort the primitives along the above coordinate direction. - PrimitiveList sortedPrimitives(a_primitives); - - std::sort( - sortedPrimitives.begin(), sortedPrimitives.end(), - [splitDir](const std::shared_ptr>& f1, const std::shared_ptr>& f2) -> bool { - return f1->getCentroid(splitDir) < f2->getCentroid(splitDir); - }); + const size_t splitDir = (hi - lo).maxDir(true); - return EBGeometry::DCEL::equalCounts(sortedPrimitives); -}; + // Sort the primitives along the above coordinate direction. + PrimitiveList sortedPrimitives(a_primitives); -/*! - @brief Partitioner function for subdividing into K sub-volumes with - approximately the same number of primitives. - @details Basically the same as chunkPartitioner, except that the centroids are - based on the bounding volumes' centroids. - @param[in] a_primitives List of primitives to partition into sub-bounding - volumes -*/ -template -EBGeometry::BVH::PartitionerT, BV, K> bvPartitioner = - [](const PrimitiveList& a_primitives) -> std::array, K> { - Vec3T lo = Vec3T::max(); - Vec3T hi = -Vec3T::max(); + std::sort(sortedPrimitives.begin(), + sortedPrimitives.end(), + [splitDir](const std::shared_ptr>& f1, const std::shared_ptr>& f2) -> bool { + return f1->getCentroid(splitDir) < f2->getCentroid(splitDir); + }); - // Pack primitives and their bounding volumes together. - using P = std::pair>, BV>; + return EBGeometry::DCEL::equalCounts(sortedPrimitives); + }; - std::vector

primsAndBVs; + /*! + @brief Partitioner function for subdividing into K sub-volumes with + approximately the same number of primitives. + @details Basically the same as chunkPartitioner, except that the centroids are + based on the bounding volumes' centroids. + @param[in] a_primitives List of primitives to partition into sub-bounding + volumes + */ + template + EBGeometry::BVH::PartitionerT, BV, K> bvPartitioner = + [](const PrimitiveList& a_primitives) -> std::array, K> { + Vec3T lo = Vec3T::max(); + Vec3T hi = -Vec3T::max(); - for (const auto& p : a_primitives) { - const BV bv(p->getAllVertexCoordinates()); + // Pack primitives and their bounding volumes together. + using P = std::pair>, BV>; - primsAndBVs.emplace_back(p, bv); + std::vector

primsAndBVs; - lo = min(lo, bv.getCentroid()); - hi = max(hi, bv.getCentroid()); - } + for (const auto& p : a_primitives) { + const BV bv(p->getAllVertexCoordinates()); - const size_t splitDir = (hi - lo).maxDir(true); + primsAndBVs.emplace_back(p, bv); - // Sort the primitives based on the centroid location of their BVs. - std::sort(primsAndBVs.begin(), primsAndBVs.end(), [splitDir](const P& p1, const P& p2) { - return (p1.second).getCentroid()[splitDir] < (p2.second).getCentroid()[splitDir]; - }); + lo = min(lo, bv.getCentroid()); + hi = max(hi, bv.getCentroid()); + } - // Unpack the vector and partition into equal counts. - PrimitiveList sortedPrimitives; - for (const auto& p : primsAndBVs) { - sortedPrimitives.emplace_back(p.first); - } + const size_t splitDir = (hi - lo).maxDir(true); - primsAndBVs.resize(0); + // Sort the primitives based on the centroid location of their BVs. + std::sort(primsAndBVs.begin(), primsAndBVs.end(), [splitDir](const P& p1, const P& p2) { + return (p1.second).getCentroid()[splitDir] < (p2.second).getCentroid()[splitDir]; + }); - return EBGeometry::DCEL::equalCounts(sortedPrimitives); -}; + // Unpack the vector and partition into equal counts. + PrimitiveList sortedPrimitives; + for (const auto& p : primsAndBVs) { + sortedPrimitives.emplace_back(p.first); + } + + primsAndBVs.resize(0); + + return EBGeometry::DCEL::equalCounts(sortedPrimitives); + }; + + /*! + @brief Partitioner function for subdividing into K sub-volumes, partitioning + on the primitive centroid midpoint(s). + @param[in] a_primitives List of primitives to partition into sub-bounding + volumes + */ + template + EBGeometry::BVH::PartitionerT, BV, K> centroidPartitioner = + [](const PrimitiveList& a_primitives) -> std::array, K> { + Vec3T lo = Vec3T::max(); + Vec3T hi = -Vec3T::max(); + for (const auto& p : a_primitives) { + lo = min(lo, p->getCentroid()); + hi = max(hi, p->getCentroid()); + } + + const size_t splitDir = (hi - lo).maxDir(true); + const T delta = (hi - lo)[splitDir] / K; + + std::array boundsLo; + std::array boundsHi; -/*! - @brief Partitioner function for subdividing into K sub-volumes, partitioning - on the primitive centroid midpoint(s). - @param[in] a_primitives List of primitives to partition into sub-bounding - volumes -*/ -template -EBGeometry::BVH::PartitionerT, BV, K> centroidPartitioner = - [](const PrimitiveList& a_primitives) -> std::array, K> { - Vec3T lo = Vec3T::max(); - Vec3T hi = -Vec3T::max(); - for (const auto& p : a_primitives) { - lo = min(lo, p->getCentroid()); - hi = max(hi, p->getCentroid()); - } - - const size_t splitDir = (hi - lo).maxDir(true); - const T delta = (hi - lo)[splitDir] / K; - - std::array boundsLo; - std::array boundsHi; - - for (size_t k = 0; k < K; k++) { - boundsLo[k] = lo[splitDir] + delta * k; - boundsHi[k] = lo[splitDir] + delta * (k + 1); - } - - // Given coord, find the bin. - auto getBin = [&](const T& coord) -> size_t { for (size_t k = 0; k < K; k++) { - if (coord >= boundsLo[k] && coord <= boundsHi[k]) { - return k; - } + boundsLo[k] = lo[splitDir] + delta * k; + boundsHi[k] = lo[splitDir] + delta * (k + 1); } - return K - 1; - }; + // Given coord, find the bin. + auto getBin = [&](const T& coord) -> size_t { + for (size_t k = 0; k < K; k++) { + if (coord >= boundsLo[k] && coord <= boundsHi[k]) { + return k; + } + } - // Put primitives in their respective bins. - std::array, K> chunks; + return K - 1; + }; - for (const auto& p : a_primitives) { - const size_t k = getBin(p->getCentroid()[splitDir]); - chunks[k].push_back(p); - } + // Put primitives in their respective bins. + std::array, K> chunks; - // The centroid-based partitioner can end up with no primitives in one of the - // leaves (a rare case). Use a different partitioner in that case. - if (!(EBGeometry::DCEL::validChunks(chunks))) { - chunks = EBGeometry::DCEL::chunkPartitioner(a_primitives); - } + for (const auto& p : a_primitives) { + const size_t k = getBin(p->getCentroid()[splitDir]); + chunks[k].push_back(p); + } - return chunks; -}; + // The centroid-based partitioner can end up with no primitives in one of the + // leaves (a rare case). Use a different partitioner in that case. + if (!(EBGeometry::DCEL::validChunks(chunks))) { + chunks = EBGeometry::DCEL::chunkPartitioner(a_primitives); + } -/*! - @brief Alias for default partitioner. -*/ -template -EBGeometry::BVH::PartitionerT, BV, K> defaultPartitioner = - EBGeometry::DCEL::chunkPartitioner; + return chunks; + }; + + /*! + @brief Alias for default partitioner. + */ + template + EBGeometry::BVH::PartitionerT, BV, K> defaultPartitioner = + EBGeometry::DCEL::chunkPartitioner; } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_Edge.hpp b/Source/EBGeometry_DCEL_Edge.hpp index 65a6b9e9..47687448 100644 --- a/Source/EBGeometry_DCEL_Edge.hpp +++ b/Source/EBGeometry_DCEL_Edge.hpp @@ -24,348 +24,347 @@ namespace DCEL { -// Forward declare classes. -template -class VertexT; -template -class EdgeT; -template -class FaceT; -template -class EdgeIteratorT; - -/*! - @brief Class which represents a half-edge in a double-edge connected list - (DCEL). - @details This class is used in DCEL functionality which stores polygonal - surfaces in a mesh. The information contain in an EdgeT object contains the - necessary object for logically circulating the inside of a polygon face. This - means that a polygon face has a double-connected list of half-edges which - circulate the interior of the face. The EdgeT object is such a half-edge; it - represents the outgoing half-edge from a vertex, located such that it can be - logically represented as a half edge on the "inside" of a polygon face. It - contains pointers to the polygon face, next half edge, and the previous half - edge. It also contains a pointer to the "pair" half edge, i.e. the - corresponding half-edge on the other face that shares this edge. Since this - class is used with DCEL functionality and signed distance fields, this class - also has a signed distance function and thus a "normal vector". For numericaly - efficiency, some extra storage is also allocated (such as the vector between - the starting vertex and the end vertex). - @note The normal vector is outgoing, i.e. a point x is "outside" if the dot - product between n and (x - x0) is positive. -*/ -template -class EdgeT -{ -public: - /*! - @brief Alias to cut down on typing - */ - using Vec3 = Vec3T; - - /*! - @brief Alias to cut down on typing - */ - using Vertex = VertexT; - - /*! - @brief Alias to cut down on typing - */ - using Edge = EdgeT; - - /*! - @brief Alias to cut down on typing - */ - using Face = FaceT; - - /*! - @brief Alias to cut down on typing - */ - using VertexPtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing - */ - using EdgePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing - */ - using FacePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing - */ - using EdgeIterator = EdgeIteratorT; - - /*! - @brief Default constructor. Sets all pointers to zero and vectors to zero - vectors - */ - EdgeT(); - - /*! - @brief Copy constructor. Copies all information from the other half-edge. - @param[in] a_otherEdge Other edge - */ - EdgeT(const Edge& a_otherEdge); - - /*! - @brief Partial constructor. Calls the default constructor but sets the - starting vertex. - @param[in] a_vertex Starting vertex. - */ - EdgeT(const VertexPtr& a_vertex); - - /*! - @brief Destructor (does nothing) - */ - ~EdgeT(); - - /*! - @brief Define function. Sets the starting vertex, edges, and normal vectors - @param[in] a_vertex Starting vertex - @param[in] a_pairEdge Pair half-edge - @param[in] a_nextEdge Next half-edge - @param[in] a_previousEdge Previous half-edge - @param[in] a_normal Edge normal vector - */ - inline void - define( - const VertexPtr& a_vertex, - const EdgePtr& a_pairEdge, - const EdgePtr& a_nextEdge, - const EdgePtr& a_previousEdge, - const Vec3 a_normal) noexcept; - - /*! - @brief Set the starting vertex - @param[in] a_vertex Starting vertex - */ - inline void - setVertex(const VertexPtr& a_vertex) noexcept; - - /*! - @brief Set the pair edge - @param[in] a_pairEdge Pair edge - */ - inline void - setPairEdge(const EdgePtr& a_pairEdge) noexcept; - - /*! - @brief Set the next edge - @param[in] a_nextEdge Next edge - */ - inline void - setNextEdge(const EdgePtr& a_nextEdge) noexcept; - - /*! - @brief Set the previous edge - @param[in] a_previousEdge Previous edge - */ - inline void - setPreviousEdge(const EdgePtr& a_previousEdge) noexcept; - - /*! - @brief Set the pointer to this half-edge's face. - */ - inline void - setFace(const FacePtr& a_face) noexcept; - - /*! - @brief Compute edge normal and edge length (for performance reasons) - */ - inline void - reconcile() noexcept; - - /*! - @brief Get modifiable starting vertex - @return Returns m_vertex - */ - inline VertexPtr& - getVertex() noexcept; - - /*! - @brief Get immutable starting vertex - @return Returns m_vertex - */ - inline const VertexPtr& - getVertex() const noexcept; - - /*! - @brief Get modifiable end vertex - @return Returns the next half-edge's starting vertex - */ - inline VertexPtr& - getOtherVertex() noexcept; - - /*! - @brief Get immutable end vertex - @return Returns the next half-edge's starting vertex - */ - inline const VertexPtr& - getOtherVertex() const noexcept; - - /*! - @brief Get modifiable pair edge - @return Returns the pair edge - */ - inline EdgePtr& - getPairEdge() noexcept; - - /*! - @brief Get immutable pair edge - @return Returns the pair edge - */ - inline const EdgePtr& - getPairEdge() const noexcept; - - /*! - @brief Get modifiable previous edge - @return Returns the previous edge - */ - inline EdgePtr& - getPreviousEdge() noexcept; - - /*! - @brief Get immutable previous edge - @return Returns the previous edge - */ - inline const EdgePtr& - getPreviousEdge() const noexcept; - - /*! - @brief Get modifiable next edge - @return Returns the next edge - */ - inline EdgePtr& - getNextEdge() noexcept; - - /*! - @brief Get immutable next edge - @return Returns the next edge - */ - inline const EdgePtr& - getNextEdge() const noexcept; - - /*! - @brief Get modifiable half-edge normal vector - */ - inline Vec3T& - getNormal() noexcept; - - /*! - @brief Get immutable half-edge normal vector - */ - inline const Vec3T& - getNormal() const noexcept; - - /*! - @brief Get modifiable half-edge face - */ - inline FacePtr& - getFace() noexcept; - - /*! - @brief Get immutable half-edge face - */ - inline const FacePtr& - getFace() const noexcept; - - /*! - @brief Get the signed distance to this half edge - @details This routine will check if the input point projects to the edge or - one of the vertices. If it projectes to one of the vertices we compute the - signed distance to the corresponding vertex. Otherwise we compute the - projection to the edge and compute the sign from the normal vector. - */ - inline T - signedDistance(const Vec3& a_x0) const noexcept; - - /*! - @brief Get the signed distance to this half edge - @details This routine will check if the input point projects to the edge or - one of the vertices. If it projectes to one of the vertices we compute the - squared distance to the corresponding vertex. Otherwise we compute the - squared distance of the projection to the edge. This is faster than - signedDistance() - */ - inline T - unsignedDistance2(const Vec3& a_x0) const noexcept; - -protected: - /*! - @brief Half-edge normal vector - @details Computed in computeNormal which sets the normal vector to be the - average of the normal vector of the connected faces - */ - Vec3 m_normal; - - /*! - @brief Vector from the starting vertex to the end vertex. Exists for - performance reasons. - */ - Vec3 m_x2x1; - - /*! - @brief Squared inverse edge length. Exists for performance reasons. - */ - T m_invLen2; - - /*! - @brief Starting vertex - */ - VertexPtr m_vertex; - - /*! - @brief Pair edge - */ - EdgePtr m_pairEdge; - - /*! - @brief Previous edge - */ - EdgePtr m_previousEdge; - - /*! - @brief Next edge - */ - EdgePtr m_nextEdge; - - /*! - @brief Enclosing polygon face - */ - FacePtr m_face; - - /*! - @brief Returns the "projection" of a point to an edge. - @details This function parametrizes the edge as x(t) = x0 + (x1-x0)*t and - returns where on the this edge the point a_x0 projects. If projects onto the - edge if t = [0,1] and to one of the start/end vertices otherwise. - */ - inline T - projectPointToEdge(const Vec3& a_x0) const noexcept; - - /*! - @brief Normalize the normal vector, ensuring it has length 1 - */ - inline void - normalizeNormalVector() noexcept; - - /*! - @brief Compute the edge length. - @details This computes the vector m_x2x1 (vector from starting vertex to end - vertex) and the inverse length squared. - */ - inline void - computeEdgeLength() noexcept; - - /*! - @brief Compute normal vector as average of face normals - */ - inline void - computeNormal() noexcept; -}; + // Forward declare classes. + template + class VertexT; + template + class EdgeT; + template + class FaceT; + template + class EdgeIteratorT; + + /*! + @brief Class which represents a half-edge in a double-edge connected list + (DCEL). + @details This class is used in DCEL functionality which stores polygonal + surfaces in a mesh. The information contain in an EdgeT object contains the + necessary object for logically circulating the inside of a polygon face. This + means that a polygon face has a double-connected list of half-edges which + circulate the interior of the face. The EdgeT object is such a half-edge; it + represents the outgoing half-edge from a vertex, located such that it can be + logically represented as a half edge on the "inside" of a polygon face. It + contains pointers to the polygon face, next half edge, and the previous half + edge. It also contains a pointer to the "pair" half edge, i.e. the + corresponding half-edge on the other face that shares this edge. Since this + class is used with DCEL functionality and signed distance fields, this class + also has a signed distance function and thus a "normal vector". For numericaly + efficiency, some extra storage is also allocated (such as the vector between + the starting vertex and the end vertex). + @note The normal vector is outgoing, i.e. a point x is "outside" if the dot + product between n and (x - x0) is positive. + */ + template + class EdgeT + { + public: + /*! + @brief Alias to cut down on typing + */ + using Vec3 = Vec3T; + + /*! + @brief Alias to cut down on typing + */ + using Vertex = VertexT; + + /*! + @brief Alias to cut down on typing + */ + using Edge = EdgeT; + + /*! + @brief Alias to cut down on typing + */ + using Face = FaceT; + + /*! + @brief Alias to cut down on typing + */ + using VertexPtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing + */ + using EdgePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing + */ + using FacePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing + */ + using EdgeIterator = EdgeIteratorT; + + /*! + @brief Default constructor. Sets all pointers to zero and vectors to zero + vectors + */ + EdgeT(); + + /*! + @brief Copy constructor. Copies all information from the other half-edge. + @param[in] a_otherEdge Other edge + */ + EdgeT(const Edge& a_otherEdge); + + /*! + @brief Partial constructor. Calls the default constructor but sets the + starting vertex. + @param[in] a_vertex Starting vertex. + */ + EdgeT(const VertexPtr& a_vertex); + + /*! + @brief Destructor (does nothing) + */ + ~EdgeT(); + + /*! + @brief Define function. Sets the starting vertex, edges, and normal vectors + @param[in] a_vertex Starting vertex + @param[in] a_pairEdge Pair half-edge + @param[in] a_nextEdge Next half-edge + @param[in] a_previousEdge Previous half-edge + @param[in] a_normal Edge normal vector + */ + inline void + define(const VertexPtr& a_vertex, + const EdgePtr& a_pairEdge, + const EdgePtr& a_nextEdge, + const EdgePtr& a_previousEdge, + const Vec3 a_normal) noexcept; + + /*! + @brief Set the starting vertex + @param[in] a_vertex Starting vertex + */ + inline void + setVertex(const VertexPtr& a_vertex) noexcept; + + /*! + @brief Set the pair edge + @param[in] a_pairEdge Pair edge + */ + inline void + setPairEdge(const EdgePtr& a_pairEdge) noexcept; + + /*! + @brief Set the next edge + @param[in] a_nextEdge Next edge + */ + inline void + setNextEdge(const EdgePtr& a_nextEdge) noexcept; + + /*! + @brief Set the previous edge + @param[in] a_previousEdge Previous edge + */ + inline void + setPreviousEdge(const EdgePtr& a_previousEdge) noexcept; + + /*! + @brief Set the pointer to this half-edge's face. + */ + inline void + setFace(const FacePtr& a_face) noexcept; + + /*! + @brief Compute edge normal and edge length (for performance reasons) + */ + inline void + reconcile() noexcept; + + /*! + @brief Get modifiable starting vertex + @return Returns m_vertex + */ + inline VertexPtr& + getVertex() noexcept; + + /*! + @brief Get immutable starting vertex + @return Returns m_vertex + */ + inline const VertexPtr& + getVertex() const noexcept; + + /*! + @brief Get modifiable end vertex + @return Returns the next half-edge's starting vertex + */ + inline VertexPtr& + getOtherVertex() noexcept; + + /*! + @brief Get immutable end vertex + @return Returns the next half-edge's starting vertex + */ + inline const VertexPtr& + getOtherVertex() const noexcept; + + /*! + @brief Get modifiable pair edge + @return Returns the pair edge + */ + inline EdgePtr& + getPairEdge() noexcept; + + /*! + @brief Get immutable pair edge + @return Returns the pair edge + */ + inline const EdgePtr& + getPairEdge() const noexcept; + + /*! + @brief Get modifiable previous edge + @return Returns the previous edge + */ + inline EdgePtr& + getPreviousEdge() noexcept; + + /*! + @brief Get immutable previous edge + @return Returns the previous edge + */ + inline const EdgePtr& + getPreviousEdge() const noexcept; + + /*! + @brief Get modifiable next edge + @return Returns the next edge + */ + inline EdgePtr& + getNextEdge() noexcept; + + /*! + @brief Get immutable next edge + @return Returns the next edge + */ + inline const EdgePtr& + getNextEdge() const noexcept; + + /*! + @brief Get modifiable half-edge normal vector + */ + inline Vec3T& + getNormal() noexcept; + + /*! + @brief Get immutable half-edge normal vector + */ + inline const Vec3T& + getNormal() const noexcept; + + /*! + @brief Get modifiable half-edge face + */ + inline FacePtr& + getFace() noexcept; + + /*! + @brief Get immutable half-edge face + */ + inline const FacePtr& + getFace() const noexcept; + + /*! + @brief Get the signed distance to this half edge + @details This routine will check if the input point projects to the edge or + one of the vertices. If it projectes to one of the vertices we compute the + signed distance to the corresponding vertex. Otherwise we compute the + projection to the edge and compute the sign from the normal vector. + */ + inline T + signedDistance(const Vec3& a_x0) const noexcept; + + /*! + @brief Get the signed distance to this half edge + @details This routine will check if the input point projects to the edge or + one of the vertices. If it projectes to one of the vertices we compute the + squared distance to the corresponding vertex. Otherwise we compute the + squared distance of the projection to the edge. This is faster than + signedDistance() + */ + inline T + unsignedDistance2(const Vec3& a_x0) const noexcept; + + protected: + /*! + @brief Half-edge normal vector + @details Computed in computeNormal which sets the normal vector to be the + average of the normal vector of the connected faces + */ + Vec3 m_normal; + + /*! + @brief Vector from the starting vertex to the end vertex. Exists for + performance reasons. + */ + Vec3 m_x2x1; + + /*! + @brief Squared inverse edge length. Exists for performance reasons. + */ + T m_invLen2; + + /*! + @brief Starting vertex + */ + VertexPtr m_vertex; + + /*! + @brief Pair edge + */ + EdgePtr m_pairEdge; + + /*! + @brief Previous edge + */ + EdgePtr m_previousEdge; + + /*! + @brief Next edge + */ + EdgePtr m_nextEdge; + + /*! + @brief Enclosing polygon face + */ + FacePtr m_face; + + /*! + @brief Returns the "projection" of a point to an edge. + @details This function parametrizes the edge as x(t) = x0 + (x1-x0)*t and + returns where on the this edge the point a_x0 projects. If projects onto the + edge if t = [0,1] and to one of the start/end vertices otherwise. + */ + inline T + projectPointToEdge(const Vec3& a_x0) const noexcept; + + /*! + @brief Normalize the normal vector, ensuring it has length 1 + */ + inline void + normalizeNormalVector() noexcept; + + /*! + @brief Compute the edge length. + @details This computes the vector m_x2x1 (vector from starting vertex to end + vertex) and the inverse length squared. + */ + inline void + computeEdgeLength() noexcept; + + /*! + @brief Compute normal vector as average of face normals + */ + inline void + computeNormal() noexcept; + }; } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_EdgeImplem.hpp b/Source/EBGeometry_DCEL_EdgeImplem.hpp index 945801ee..66777b15 100644 --- a/Source/EBGeometry_DCEL_EdgeImplem.hpp +++ b/Source/EBGeometry_DCEL_EdgeImplem.hpp @@ -22,284 +22,284 @@ namespace DCEL { -template -inline EdgeT::EdgeT() -{ - m_face = nullptr; - m_vertex = nullptr; - m_pairEdge = nullptr; - m_nextEdge = nullptr; - m_previousEdge = nullptr; - m_normal = Vec3::zero(); - m_x2x1 = Vec3::zero(); - m_invLen2 = 0.0; -} - -template -inline EdgeT::EdgeT(const VertexPtr& a_vertex) : EdgeT() -{ - m_vertex = a_vertex; -} - -template -inline EdgeT::EdgeT(const Edge& a_otherEdge) : EdgeT() -{ - m_face = a_otherEdge.m_face; - m_vertex = a_otherEdge.m_vertex; - m_pairEdge = a_otherEdge.m_pairEdge; - m_nextEdge = a_otherEdge.m_nextEdge; - m_previousEdge = a_otherEdge.m_previousEdge; - m_normal = a_otherEdge.m_normal; - m_x2x1 = a_otherEdge.m_x2x1; - m_invLen2 = a_otherEdge.m_invLen2; -} - -template -inline EdgeT::~EdgeT() -{ -} - -template -inline void -EdgeT::define( - const VertexPtr& a_vertex, - const EdgePtr& a_pairEdge, - const EdgePtr& a_nextEdge, - const EdgePtr& a_previousEdge, - const Vec3 a_normal) noexcept -{ - m_vertex = a_vertex; - m_pairEdge = a_pairEdge; - m_nextEdge = a_nextEdge; - m_previousEdge = a_previousEdge; - m_normal = a_normal; -} - -template -inline void -EdgeT::setVertex(const VertexPtr& a_vertex) noexcept -{ - m_vertex = a_vertex; -} - -template -inline void -EdgeT::setPairEdge(const EdgePtr& a_pairEdge) noexcept -{ - m_pairEdge = a_pairEdge; -} - -template -inline void -EdgeT::setNextEdge(const EdgePtr& a_nextEdge) noexcept -{ - m_nextEdge = a_nextEdge; -} - -template -inline void -EdgeT::setPreviousEdge(const EdgePtr& a_previousEdge) noexcept -{ - m_previousEdge = a_previousEdge; -} - -template -inline void -EdgeT::setFace(const FacePtr& a_face) noexcept -{ - m_face = a_face; -} - -template -inline void -EdgeT::normalizeNormalVector() noexcept -{ - m_normal = m_normal / m_normal.length(); -} - -template -inline void -EdgeT::computeEdgeLength() noexcept -{ - const auto& x1 = this->getVertex()->getPosition(); - const auto& x2 = this->getOtherVertex()->getPosition(); - - m_x2x1 = x2 - x1; - - const auto len2 = m_x2x1.dot(m_x2x1); - - m_invLen2 = 1. / len2; -} - -template -inline void -EdgeT::computeNormal() noexcept -{ - - m_normal = m_face->getNormal(); - - if (m_pairEdge) { - m_normal += m_pairEdge->getFace()->getNormal(); + template + inline EdgeT::EdgeT() + { + m_face = nullptr; + m_vertex = nullptr; + m_pairEdge = nullptr; + m_nextEdge = nullptr; + m_previousEdge = nullptr; + m_normal = Vec3::zero(); + m_x2x1 = Vec3::zero(); + m_invLen2 = 0.0; } - this->normalizeNormalVector(); -} - -template -inline void -EdgeT::reconcile() noexcept -{ - this->computeNormal(); - this->computeEdgeLength(); -} - -template -inline std::shared_ptr>& -EdgeT::getVertex() noexcept -{ - return (m_vertex); -} - -template -inline const std::shared_ptr>& -EdgeT::getVertex() const noexcept -{ - return (m_vertex); -} - -template -inline std::shared_ptr>& -EdgeT::getOtherVertex() noexcept -{ - return (m_nextEdge->getVertex()); -} - -template -inline const std::shared_ptr>& -EdgeT::getOtherVertex() const noexcept -{ - return (m_nextEdge->getVertex()); -} - -template -inline std::shared_ptr>& -EdgeT::getPairEdge() noexcept -{ - return (m_pairEdge); -} - -template -inline const std::shared_ptr>& -EdgeT::getPairEdge() const noexcept -{ - return (m_pairEdge); -} - -template -inline std::shared_ptr>& -EdgeT::getPreviousEdge() noexcept -{ - return (m_previousEdge); -} - -template -inline const std::shared_ptr>& -EdgeT::getPreviousEdge() const noexcept -{ - return (m_previousEdge); -} - -template -inline std::shared_ptr>& -EdgeT::getNextEdge() noexcept -{ - return (m_nextEdge); -} - -template -inline const std::shared_ptr>& -EdgeT::getNextEdge() const noexcept -{ - return (m_nextEdge); -} - -template -inline Vec3T& -EdgeT::getNormal() noexcept -{ - return (m_normal); -} - -template -inline const Vec3T& -EdgeT::getNormal() const noexcept -{ - return (m_normal); -} - -template -inline std::shared_ptr>& -EdgeT::getFace() noexcept -{ - return (m_face); -} - -template -inline const std::shared_ptr>& -EdgeT::getFace() const noexcept -{ - return (m_face); -} - -template -inline T -EdgeT::projectPointToEdge(const Vec3& a_x0) const noexcept -{ - const auto p = a_x0 - m_vertex->getPosition(); - - return p.dot(m_x2x1) * m_invLen2; -} - -template -inline T -EdgeT::signedDistance(const Vec3& a_x0) const noexcept -{ - const T t = this->projectPointToEdge(a_x0); - - T retval; - if (t <= 0.0) { // Closest point is the starting vertex - retval = this->getVertex()->signedDistance(a_x0); - } else if (t >= 1.0) { // Closest point is the end vertex - retval = this->getOtherVertex()->signedDistance(a_x0); - } else { // Closest point is the edge itself. - const Vec3 linePoint = m_vertex->getPosition() + t * m_x2x1; - const Vec3 delta = a_x0 - linePoint; - const T dot = m_normal.dot(delta); - - const int sgn = (dot > 0.0) ? 1 : -1; - - retval = sgn * delta.length(); + template + inline EdgeT::EdgeT(const VertexPtr& a_vertex) : EdgeT() + { + m_vertex = a_vertex; } - return retval; -} + template + inline EdgeT::EdgeT(const Edge& a_otherEdge) : EdgeT() + { + m_face = a_otherEdge.m_face; + m_vertex = a_otherEdge.m_vertex; + m_pairEdge = a_otherEdge.m_pairEdge; + m_nextEdge = a_otherEdge.m_nextEdge; + m_previousEdge = a_otherEdge.m_previousEdge; + m_normal = a_otherEdge.m_normal; + m_x2x1 = a_otherEdge.m_x2x1; + m_invLen2 = a_otherEdge.m_invLen2; + } + + template + inline EdgeT::~EdgeT() + {} + + template + inline void + EdgeT::define(const VertexPtr& a_vertex, + const EdgePtr& a_pairEdge, + const EdgePtr& a_nextEdge, + const EdgePtr& a_previousEdge, + const Vec3 a_normal) noexcept + { + m_vertex = a_vertex; + m_pairEdge = a_pairEdge; + m_nextEdge = a_nextEdge; + m_previousEdge = a_previousEdge; + m_normal = a_normal; + } + + template + inline void + EdgeT::setVertex(const VertexPtr& a_vertex) noexcept + { + m_vertex = a_vertex; + } + + template + inline void + EdgeT::setPairEdge(const EdgePtr& a_pairEdge) noexcept + { + m_pairEdge = a_pairEdge; + } + + template + inline void + EdgeT::setNextEdge(const EdgePtr& a_nextEdge) noexcept + { + m_nextEdge = a_nextEdge; + } + + template + inline void + EdgeT::setPreviousEdge(const EdgePtr& a_previousEdge) noexcept + { + m_previousEdge = a_previousEdge; + } + + template + inline void + EdgeT::setFace(const FacePtr& a_face) noexcept + { + m_face = a_face; + } + + template + inline void + EdgeT::normalizeNormalVector() noexcept + { + m_normal = m_normal / m_normal.length(); + } -template -inline T -EdgeT::unsignedDistance2(const Vec3& a_x0) const noexcept -{ - T t = this->projectPointToEdge(a_x0); + template + inline void + EdgeT::computeEdgeLength() noexcept + { + const auto& x1 = this->getVertex()->getPosition(); + const auto& x2 = this->getOtherVertex()->getPosition(); - constexpr T zero = 0.0; - constexpr T one = 1.0; + m_x2x1 = x2 - x1; - t = std::min(std::max(zero, t), one); // Edge is on t=[0,1]. + const auto len2 = m_x2x1.dot(m_x2x1); - const Vec3T linePoint = m_vertex->getPosition() + t * m_x2x1; - const Vec3T delta = a_x0 - linePoint; + m_invLen2 = 1. / len2; + } + + template + inline void + EdgeT::computeNormal() noexcept + { + + m_normal = m_face->getNormal(); + + if (m_pairEdge) { + m_normal += m_pairEdge->getFace()->getNormal(); + } + + this->normalizeNormalVector(); + } + + template + inline void + EdgeT::reconcile() noexcept + { + this->computeNormal(); + this->computeEdgeLength(); + } + + template + inline std::shared_ptr>& + EdgeT::getVertex() noexcept + { + return (m_vertex); + } + + template + inline const std::shared_ptr>& + EdgeT::getVertex() const noexcept + { + return (m_vertex); + } + + template + inline std::shared_ptr>& + EdgeT::getOtherVertex() noexcept + { + return (m_nextEdge->getVertex()); + } + + template + inline const std::shared_ptr>& + EdgeT::getOtherVertex() const noexcept + { + return (m_nextEdge->getVertex()); + } - return delta.dot(delta); -} + template + inline std::shared_ptr>& + EdgeT::getPairEdge() noexcept + { + return (m_pairEdge); + } + + template + inline const std::shared_ptr>& + EdgeT::getPairEdge() const noexcept + { + return (m_pairEdge); + } + + template + inline std::shared_ptr>& + EdgeT::getPreviousEdge() noexcept + { + return (m_previousEdge); + } + + template + inline const std::shared_ptr>& + EdgeT::getPreviousEdge() const noexcept + { + return (m_previousEdge); + } + + template + inline std::shared_ptr>& + EdgeT::getNextEdge() noexcept + { + return (m_nextEdge); + } + + template + inline const std::shared_ptr>& + EdgeT::getNextEdge() const noexcept + { + return (m_nextEdge); + } + + template + inline Vec3T& + EdgeT::getNormal() noexcept + { + return (m_normal); + } + + template + inline const Vec3T& + EdgeT::getNormal() const noexcept + { + return (m_normal); + } + + template + inline std::shared_ptr>& + EdgeT::getFace() noexcept + { + return (m_face); + } + + template + inline const std::shared_ptr>& + EdgeT::getFace() const noexcept + { + return (m_face); + } + + template + inline T + EdgeT::projectPointToEdge(const Vec3& a_x0) const noexcept + { + const auto p = a_x0 - m_vertex->getPosition(); + + return p.dot(m_x2x1) * m_invLen2; + } + + template + inline T + EdgeT::signedDistance(const Vec3& a_x0) const noexcept + { + const T t = this->projectPointToEdge(a_x0); + + T retval; + if (t <= 0.0) { // Closest point is the starting vertex + retval = this->getVertex()->signedDistance(a_x0); + } + else if (t >= 1.0) { // Closest point is the end vertex + retval = this->getOtherVertex()->signedDistance(a_x0); + } + else { // Closest point is the edge itself. + const Vec3 linePoint = m_vertex->getPosition() + t * m_x2x1; + const Vec3 delta = a_x0 - linePoint; + const T dot = m_normal.dot(delta); + + const int sgn = (dot > 0.0) ? 1 : -1; + + retval = sgn * delta.length(); + } + + return retval; + } + + template + inline T + EdgeT::unsignedDistance2(const Vec3& a_x0) const noexcept + { + T t = this->projectPointToEdge(a_x0); + + constexpr T zero = 0.0; + constexpr T one = 1.0; + + t = std::min(std::max(zero, t), one); // Edge is on t=[0,1]. + + const Vec3T linePoint = m_vertex->getPosition() + t * m_x2x1; + const Vec3T delta = a_x0 - linePoint; + + return delta.dot(delta); + } } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_Face.hpp b/Source/EBGeometry_DCEL_Face.hpp index 672dccc1..6c910045 100644 --- a/Source/EBGeometry_DCEL_Face.hpp +++ b/Source/EBGeometry_DCEL_Face.hpp @@ -26,338 +26,338 @@ namespace DCEL { -// Forward declarations of other DCEL functionality. -template -class VertexT; -template -class EdgeT; -template -class FaceT; -template -class EdgeIteratorT; - -/*! - @brief Class which represents a polygon face in a double-edge connected list - (DCEL). - @details This class is a polygon face in a DCEL mesh. It contains pointer - storage to one of the half-edges that circulate the inside of the polygon - face, as well as having a normal vector, a centroid, and an area. This class - supports signed distance computations. These computations require algorithms - that compute e.g. the winding number of the polygon, or the number of times a - ray cast passes through it. Thus, one of its central features is that it can - be embedded in 2D by projecting it along the cardinal direction of its normal - vector. To be fully consistent with a DCEL structure the class stores a - reference to one of its half edges, but for performance reasons it also stores - references to the other half edges. - @note To compute the distance from a point to the face one must determine if - the point projects "inside" or "outside" the polygon. There are several - algorithms for this, and by default this class uses a crossing number - algorithm. Other algorithms can be set in setInsideOutsideAlgorithm (see - CD_DCELAlgorithms.H) -*/ -template -class FaceT -{ -public: - /*! - @brief Alias to cut down on typing - */ - using Vec3 = Vec3T; - - /*! - @brief Alias to cut down on typing - */ - using Vertex = VertexT; - - /*! - @brief Alias to cut down on typing - */ - using Edge = EdgeT; - - /*! - @brief Alias to cut down on typing - */ - using Face = FaceT; - - /*! - @brief Alias to cut down on typing - */ - using VertexPtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing - */ - using EdgePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing - */ - using FacePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing - */ - using EdgeIterator = EdgeIteratorT; - - /*! - @brief Default constructor. Sets the half-edge to zero and the - inside/outside algorithm to crossing number algorithm - */ - FaceT(); - - /*! - @brief Partial constructor. Calls default constructor but associates a - half-edge - @param[in] a_edge Half-edge - */ - FaceT(const EdgePtr& a_edge); - - /*! - @brief Partial constructor. - @details Calls default constructor but sets the normal vector and half-edge - equal to the other face's (rest is undefined) - */ - FaceT(const Face& a_otherFace); - - /*! - @brief Destructor (does nothing) - */ - ~FaceT(); - - /*! - @brief Define function which sets the normal vector and half-edge - @param[in] a_normal Normal vector - @param[in] a_edge Half edge - */ - inline void - define(const Vec3& a_normal, const EdgePtr& a_edge) noexcept; - - /*! - @brief Reconcile face. This will compute the normal vector, area, centroid, - and the 2D embedding of the polygon - @note "Everything" must be set before doing this, i.e. the face must be - complete with half edges and there can be no dangling edges. - */ - inline void - reconcile() noexcept; - - /*! - @brief Set the half edge - @param[in] a_halfEdge Half edge - */ - inline void - setHalfEdge(const EdgePtr& a_halfEdge) noexcept; - - /*! - @brief Set the inside/outside algorithm when determining if a point projects - to the inside or outside of the polygon. - @param[in] a_algorithm Desired algorithm - @note See CD_DCELAlgorithms.H for options (and CD_DCELPolyImplem.H for how - the algorithms operate). - */ - inline void - setInsideOutsideAlgorithm(typename Polygon2D::InsideOutsideAlgorithm& a_algorithm) noexcept; - - /*! - @brief Get modifiable half-edge - */ - inline EdgePtr& - getHalfEdge() noexcept; - - /*! - @brief Get immutable half-edge - */ - inline const EdgePtr& - getHalfEdge() const noexcept; - - /*! - @brief Get modifiable centroid - */ - inline Vec3T& - getCentroid() noexcept; - - /*! - @brief Get immutable centroid - */ - inline const Vec3T& - getCentroid() const noexcept; - - /*! - @brief Get modifiable centroid position in specified coordinate direction - @param[in] a_dir Coordinate direction - */ - inline T& - getCentroid(const size_t a_dir) noexcept; - - /*! - @brief Get immutable centroid position in specified coordinate direction - @param[in] a_dir Coordinate direction - */ - inline const T& - getCentroid(const size_t a_dir) const noexcept; - - /*! - @brief Get modifiable normal vector - */ - inline Vec3T& - getNormal() noexcept; - - /*! - @brief Get immutable normal vector - */ - inline const Vec3T& - getNormal() const noexcept; - - /*! - @brief Compute the signed distance to a point. - @param[in] a_x0 Point in space - @details This algorithm operates by checking if the input point projects to - the inside of the polygon. If it does then the distance is just the - projected distance onto the polygon plane and the sign is well-defined. - Otherwise, we check the distance to the edges of the polygon. - */ - inline T - signedDistance(const Vec3& a_x0) const noexcept; - - /*! - @brief Compute the unsigned squared distance to a point. - @param[in] a_x0 Point in space - @details This algorithm operates by checking if the input point projects to - the inside of the polygon. If it does then the distance is just the - projected distance onto the polygon plane. Otherwise, we check the distance - to the edges of the polygon. - */ - inline T - unsignedDistance2(const Vec3& a_x0) const noexcept; - - /*! - @brief Return the coordinates of all the vertices on this polygon. - @details This builds a list of all the vertex coordinates and returns it. - */ - inline std::vector> - getAllVertexCoordinates() const noexcept; - - /*! - @brief Return all the vertices on this polygon - @details This builds a list of all the vertices and returns it. - */ - inline std::vector - gatherVertices() const noexcept; - - /*! - @brief Get the lower-left-most coordinate of this polygon face - */ - inline Vec3T - getSmallestCoordinate() const noexcept; - - /*! - @brief Get the upper-right-most coordinate of this polygon face - */ - inline Vec3T - getHighestCoordinate() const noexcept; - -protected: - /*! - @brief This polygon's half-edge. A valid face will always have != nullptr - */ - EdgePtr m_halfEdge; - - /*! - @brief Pointers to all the half-edges of this face. Exists for performance - reasons (in signedDistance(...)) - */ - std::vector m_edges; // Exists because of performance reasons. - - /*! - @brief Polygon face area - */ - T m_area; - - /*! - @brief Polygon face normal vector - */ - Vec3 m_normal; - - /*! - @brief Polygon face centroid position - */ - Vec3 m_centroid; - - /*! - @brief 2D embedding of this polygon. This is the 2D view of the current - object projected along its normal vector cardinal. - */ - std::shared_ptr> m_poly2; - - /*! - @brief Algorithm for inside/outside tests - */ - typename Polygon2D::InsideOutsideAlgorithm m_poly2Algorithm; - - /*! - @brief Compute the area of this polygon - */ - inline void - computeArea() noexcept; - - /*! - @brief Compute the centroid position of this polygon - */ - inline void - computeCentroid() noexcept; - - /*! - @brief Compute the normal position of this polygon - */ - inline void - computeNormal() noexcept; - - /*! - @brief Compute the 2D embedding of this polygon - */ - inline void - computePolygon2D() noexcept; - - /*! - @brief Normalize the normal vector, ensuring it has a length of 1 - */ - inline void - normalizeNormalVector() noexcept; - - /*! - @brief Get the area of this polygon face - */ - inline T - getArea() noexcept; - - /*! - @brief Get the area of this polygon face - */ - inline T - getArea() const noexcept; - - /*! - @brief Compute and store all the half-edges around this polygon face - */ - inline void - computeAndStoreEdges() noexcept; - - /*! - @brief Compute the projection of a point onto the polygon face plane - @param[in] a_p Point in space - */ - inline Vec3T - projectPointIntoFacePlane(const Vec3& a_p) const noexcept; - - /*! - @brief Check if a point projects to inside or outside the polygon face - @param[in] a_p Point in space - @return Returns true if a_p projects to inside the polygon and false - otherwise. - */ - inline bool - isPointInsideFace(const Vec3& a_p) const noexcept; -}; + // Forward declarations of other DCEL functionality. + template + class VertexT; + template + class EdgeT; + template + class FaceT; + template + class EdgeIteratorT; + + /*! + @brief Class which represents a polygon face in a double-edge connected list + (DCEL). + @details This class is a polygon face in a DCEL mesh. It contains pointer + storage to one of the half-edges that circulate the inside of the polygon + face, as well as having a normal vector, a centroid, and an area. This class + supports signed distance computations. These computations require algorithms + that compute e.g. the winding number of the polygon, or the number of times a + ray cast passes through it. Thus, one of its central features is that it can + be embedded in 2D by projecting it along the cardinal direction of its normal + vector. To be fully consistent with a DCEL structure the class stores a + reference to one of its half edges, but for performance reasons it also stores + references to the other half edges. + @note To compute the distance from a point to the face one must determine if + the point projects "inside" or "outside" the polygon. There are several + algorithms for this, and by default this class uses a crossing number + algorithm. Other algorithms can be set in setInsideOutsideAlgorithm (see + CD_DCELAlgorithms.H) + */ + template + class FaceT + { + public: + /*! + @brief Alias to cut down on typing + */ + using Vec3 = Vec3T; + + /*! + @brief Alias to cut down on typing + */ + using Vertex = VertexT; + + /*! + @brief Alias to cut down on typing + */ + using Edge = EdgeT; + + /*! + @brief Alias to cut down on typing + */ + using Face = FaceT; + + /*! + @brief Alias to cut down on typing + */ + using VertexPtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing + */ + using EdgePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing + */ + using FacePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing + */ + using EdgeIterator = EdgeIteratorT; + + /*! + @brief Default constructor. Sets the half-edge to zero and the + inside/outside algorithm to crossing number algorithm + */ + FaceT(); + + /*! + @brief Partial constructor. Calls default constructor but associates a + half-edge + @param[in] a_edge Half-edge + */ + FaceT(const EdgePtr& a_edge); + + /*! + @brief Partial constructor. + @details Calls default constructor but sets the normal vector and half-edge + equal to the other face's (rest is undefined) + */ + FaceT(const Face& a_otherFace); + + /*! + @brief Destructor (does nothing) + */ + ~FaceT(); + + /*! + @brief Define function which sets the normal vector and half-edge + @param[in] a_normal Normal vector + @param[in] a_edge Half edge + */ + inline void + define(const Vec3& a_normal, const EdgePtr& a_edge) noexcept; + + /*! + @brief Reconcile face. This will compute the normal vector, area, centroid, + and the 2D embedding of the polygon + @note "Everything" must be set before doing this, i.e. the face must be + complete with half edges and there can be no dangling edges. + */ + inline void + reconcile() noexcept; + + /*! + @brief Set the half edge + @param[in] a_halfEdge Half edge + */ + inline void + setHalfEdge(const EdgePtr& a_halfEdge) noexcept; + + /*! + @brief Set the inside/outside algorithm when determining if a point projects + to the inside or outside of the polygon. + @param[in] a_algorithm Desired algorithm + @note See CD_DCELAlgorithms.H for options (and CD_DCELPolyImplem.H for how + the algorithms operate). + */ + inline void + setInsideOutsideAlgorithm(typename Polygon2D::InsideOutsideAlgorithm& a_algorithm) noexcept; + + /*! + @brief Get modifiable half-edge + */ + inline EdgePtr& + getHalfEdge() noexcept; + + /*! + @brief Get immutable half-edge + */ + inline const EdgePtr& + getHalfEdge() const noexcept; + + /*! + @brief Get modifiable centroid + */ + inline Vec3T& + getCentroid() noexcept; + + /*! + @brief Get immutable centroid + */ + inline const Vec3T& + getCentroid() const noexcept; + + /*! + @brief Get modifiable centroid position in specified coordinate direction + @param[in] a_dir Coordinate direction + */ + inline T& + getCentroid(const size_t a_dir) noexcept; + + /*! + @brief Get immutable centroid position in specified coordinate direction + @param[in] a_dir Coordinate direction + */ + inline const T& + getCentroid(const size_t a_dir) const noexcept; + + /*! + @brief Get modifiable normal vector + */ + inline Vec3T& + getNormal() noexcept; + + /*! + @brief Get immutable normal vector + */ + inline const Vec3T& + getNormal() const noexcept; + + /*! + @brief Compute the signed distance to a point. + @param[in] a_x0 Point in space + @details This algorithm operates by checking if the input point projects to + the inside of the polygon. If it does then the distance is just the + projected distance onto the polygon plane and the sign is well-defined. + Otherwise, we check the distance to the edges of the polygon. + */ + inline T + signedDistance(const Vec3& a_x0) const noexcept; + + /*! + @brief Compute the unsigned squared distance to a point. + @param[in] a_x0 Point in space + @details This algorithm operates by checking if the input point projects to + the inside of the polygon. If it does then the distance is just the + projected distance onto the polygon plane. Otherwise, we check the distance + to the edges of the polygon. + */ + inline T + unsignedDistance2(const Vec3& a_x0) const noexcept; + + /*! + @brief Return the coordinates of all the vertices on this polygon. + @details This builds a list of all the vertex coordinates and returns it. + */ + inline std::vector> + getAllVertexCoordinates() const noexcept; + + /*! + @brief Return all the vertices on this polygon + @details This builds a list of all the vertices and returns it. + */ + inline std::vector + gatherVertices() const noexcept; + + /*! + @brief Get the lower-left-most coordinate of this polygon face + */ + inline Vec3T + getSmallestCoordinate() const noexcept; + + /*! + @brief Get the upper-right-most coordinate of this polygon face + */ + inline Vec3T + getHighestCoordinate() const noexcept; + + protected: + /*! + @brief This polygon's half-edge. A valid face will always have != nullptr + */ + EdgePtr m_halfEdge; + + /*! + @brief Pointers to all the half-edges of this face. Exists for performance + reasons (in signedDistance(...)) + */ + std::vector m_edges; // Exists because of performance reasons. + + /*! + @brief Polygon face area + */ + T m_area; + + /*! + @brief Polygon face normal vector + */ + Vec3 m_normal; + + /*! + @brief Polygon face centroid position + */ + Vec3 m_centroid; + + /*! + @brief 2D embedding of this polygon. This is the 2D view of the current + object projected along its normal vector cardinal. + */ + std::shared_ptr> m_poly2; + + /*! + @brief Algorithm for inside/outside tests + */ + typename Polygon2D::InsideOutsideAlgorithm m_poly2Algorithm; + + /*! + @brief Compute the area of this polygon + */ + inline void + computeArea() noexcept; + + /*! + @brief Compute the centroid position of this polygon + */ + inline void + computeCentroid() noexcept; + + /*! + @brief Compute the normal position of this polygon + */ + inline void + computeNormal() noexcept; + + /*! + @brief Compute the 2D embedding of this polygon + */ + inline void + computePolygon2D() noexcept; + + /*! + @brief Normalize the normal vector, ensuring it has a length of 1 + */ + inline void + normalizeNormalVector() noexcept; + + /*! + @brief Get the area of this polygon face + */ + inline T + getArea() noexcept; + + /*! + @brief Get the area of this polygon face + */ + inline T + getArea() const noexcept; + + /*! + @brief Compute and store all the half-edges around this polygon face + */ + inline void + computeAndStoreEdges() noexcept; + + /*! + @brief Compute the projection of a point onto the polygon face plane + @param[in] a_p Point in space + */ + inline Vec3T + projectPointIntoFacePlane(const Vec3& a_p) const noexcept; + + /*! + @brief Check if a point projects to inside or outside the polygon face + @param[in] a_p Point in space + @return Returns true if a_p projects to inside the polygon and false + otherwise. + */ + inline bool + isPointInsideFace(const Vec3& a_p) const noexcept; + }; } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_FaceImplem.hpp b/Source/EBGeometry_DCEL_FaceImplem.hpp index 889ec188..25ddd61f 100644 --- a/Source/EBGeometry_DCEL_FaceImplem.hpp +++ b/Source/EBGeometry_DCEL_FaceImplem.hpp @@ -19,340 +19,340 @@ namespace DCEL { -template -inline FaceT::FaceT() -{ - m_halfEdge = nullptr; - m_normal = Vec3::zero(); - m_poly2Algorithm = Polygon2D::InsideOutsideAlgorithm::CrossingNumber; -} - -template -inline FaceT::FaceT(const EdgePtr& a_edge) : Face() -{ - m_halfEdge = a_edge; -} - -template -inline FaceT::FaceT(const Face& a_otherFace) : Face() -{ - m_normal = a_otherFace.getNormal(); - m_halfEdge = a_otherFace.getHalfEdge(); -} - -template -inline FaceT::~FaceT() -{ -} - -template -inline void -FaceT::define(const Vec3& a_normal, const EdgePtr& a_edge) noexcept -{ - m_normal = a_normal; - m_halfEdge = a_edge; -} - -template -inline void -FaceT::reconcile() noexcept -{ - this->computeNormal(); - this->normalizeNormalVector(); - this->computeCentroid(); - this->computeArea(); - this->computePolygon2D(); - this->computeAndStoreEdges(); -} - -template -inline void -FaceT::computeAndStoreEdges() noexcept -{ - m_edges.resize(0); - - for (EdgeIterator edgeIt(*this); edgeIt.ok(); ++edgeIt) { - m_edges.emplace_back(edgeIt()); + template + inline FaceT::FaceT() + { + m_halfEdge = nullptr; + m_normal = Vec3::zero(); + m_poly2Algorithm = Polygon2D::InsideOutsideAlgorithm::CrossingNumber; } -} - -template -inline void -FaceT::setHalfEdge(const EdgePtr& a_halfEdge) noexcept -{ - m_halfEdge = a_halfEdge; -} - -template -inline void -FaceT::normalizeNormalVector() noexcept -{ - m_normal = m_normal / m_normal.length(); -} - -template -inline void -FaceT::setInsideOutsideAlgorithm(typename Polygon2D::InsideOutsideAlgorithm& a_algorithm) noexcept -{ - m_poly2Algorithm = a_algorithm; -} - -template -inline void -FaceT::computeArea() noexcept -{ - m_area = 0.0; - - // This computes the area of any N-side polygon. - const auto vertices = this->gatherVertices(); - - for (size_t i = 0; i < vertices.size() - 1; i++) { - const auto& v1 = vertices[i]->getPosition(); - const auto& v2 = vertices[i + 1]->getPosition(); - m_area += m_normal.dot(v2.cross(v1)); + + template + inline FaceT::FaceT(const EdgePtr& a_edge) : Face() + { + m_halfEdge = a_edge; + } + + template + inline FaceT::FaceT(const Face& a_otherFace) : Face() + { + m_normal = a_otherFace.getNormal(); + m_halfEdge = a_otherFace.getHalfEdge(); + } + + template + inline FaceT::~FaceT() + {} + + template + inline void + FaceT::define(const Vec3& a_normal, const EdgePtr& a_edge) noexcept + { + m_normal = a_normal; + m_halfEdge = a_edge; + } + + template + inline void + FaceT::reconcile() noexcept + { + this->computeNormal(); + this->normalizeNormalVector(); + this->computeCentroid(); + this->computeArea(); + this->computePolygon2D(); + this->computeAndStoreEdges(); + } + + template + inline void + FaceT::computeAndStoreEdges() noexcept + { + m_edges.resize(0); + + for (EdgeIterator edgeIt(*this); edgeIt.ok(); ++edgeIt) { + m_edges.emplace_back(edgeIt()); + } + } + + template + inline void + FaceT::setHalfEdge(const EdgePtr& a_halfEdge) noexcept + { + m_halfEdge = a_halfEdge; + } + + template + inline void + FaceT::normalizeNormalVector() noexcept + { + m_normal = m_normal / m_normal.length(); } - m_area = 0.5 * std::abs(m_area); -} + template + inline void + FaceT::setInsideOutsideAlgorithm(typename Polygon2D::InsideOutsideAlgorithm& a_algorithm) noexcept + { + m_poly2Algorithm = a_algorithm; + } + + template + inline void + FaceT::computeArea() noexcept + { + m_area = 0.0; + + // This computes the area of any N-side polygon. + const auto vertices = this->gatherVertices(); + + for (size_t i = 0; i < vertices.size() - 1; i++) { + const auto& v1 = vertices[i]->getPosition(); + const auto& v2 = vertices[i + 1]->getPosition(); + m_area += m_normal.dot(v2.cross(v1)); + } + + m_area = 0.5 * std::abs(m_area); + } + + template + inline void + FaceT::computeCentroid() noexcept + { + m_centroid = Vec3::zero(); + + const auto vertices = this->gatherVertices(); + + for (const auto& v : vertices) { + m_centroid += v->getPosition(); + } + + m_centroid = m_centroid / vertices.size(); + } + + template + inline void + FaceT::computeNormal() noexcept + { + const auto vertices = this->gatherVertices(); + + const size_t N = vertices.size(); + + // To compute the normal vector we find three vertices in this polygon face. + // They span a plane, and we just compute the normal vector of that plane. + for (size_t i = 0; i < N; i++) { + const auto& x0 = vertices[i]->getPosition(); + const auto& x1 = vertices[(i + 1) % N]->getPosition(); + const auto& x2 = vertices[(i + 2) % N]->getPosition(); -template -inline void -FaceT::computeCentroid() noexcept -{ - m_centroid = Vec3::zero(); + m_normal = (x2 - x1).cross(x2 - x0); - const auto vertices = this->gatherVertices(); + if (m_normal.length() > 0.0) { + break; // Found one. + } + } + + this->normalizeNormalVector(); + } - for (const auto& v : vertices) { - m_centroid += v->getPosition(); + template + inline void + FaceT::computePolygon2D() noexcept + { + m_poly2 = std::make_shared>(m_normal, this->getAllVertexCoordinates()); } - m_centroid = m_centroid / vertices.size(); -} + template + inline T& + FaceT::getCentroid(const size_t a_dir) noexcept + { + return m_centroid[a_dir]; + } -template -inline void -FaceT::computeNormal() noexcept -{ - const auto vertices = this->gatherVertices(); + template + inline const T& + FaceT::getCentroid(const size_t a_dir) const noexcept + { + return m_centroid[a_dir]; + } - const size_t N = vertices.size(); + template + inline Vec3T& + FaceT::getCentroid() noexcept + { + return (m_centroid); + } - // To compute the normal vector we find three vertices in this polygon face. - // They span a plane, and we just compute the normal vector of that plane. - for (size_t i = 0; i < N; i++) { - const auto& x0 = vertices[i]->getPosition(); - const auto& x1 = vertices[(i + 1) % N]->getPosition(); - const auto& x2 = vertices[(i + 2) % N]->getPosition(); + template + inline const Vec3T& + FaceT::getCentroid() const noexcept + { + return (m_centroid); + } - m_normal = (x2 - x1).cross(x2 - x0); + template + inline Vec3T& + FaceT::getNormal() noexcept + { + return (m_normal); + } - if (m_normal.length() > 0.0) - break; // Found one. + template + inline const Vec3T& + FaceT::getNormal() const noexcept + { + return (m_normal); } - this->normalizeNormalVector(); -} - -template -inline void -FaceT::computePolygon2D() noexcept -{ - - // See CD_DCELPoly.H to see how the 2D embedding operates. - m_poly2 = std::make_shared>(m_normal, this->getAllVertexCoordinates()); -} - -template -inline T& -FaceT::getCentroid(const size_t a_dir) noexcept -{ - return m_centroid[a_dir]; -} - -template -inline const T& -FaceT::getCentroid(const size_t a_dir) const noexcept -{ - return m_centroid[a_dir]; -} - -template -inline Vec3T& -FaceT::getCentroid() noexcept -{ - return (m_centroid); -} - -template -inline const Vec3T& -FaceT::getCentroid() const noexcept -{ - return (m_centroid); -} - -template -inline Vec3T& -FaceT::getNormal() noexcept -{ - return (m_normal); -} - -template -inline const Vec3T& -FaceT::getNormal() const noexcept -{ - return (m_normal); -} - -template -inline T -FaceT::getArea() noexcept -{ - return (m_area); -} - -template -inline T -FaceT::getArea() const noexcept -{ - return (m_area); -} - -template -inline std::shared_ptr>& -FaceT::getHalfEdge() noexcept -{ - return (m_halfEdge); -} - -template -inline const std::shared_ptr>& -FaceT::getHalfEdge() const noexcept -{ - return (m_halfEdge); -} - -template -inline std::vector>> -FaceT::gatherVertices() const noexcept -{ - std::vector vertices; - - for (EdgeIterator iter(*this); iter.ok(); ++iter) { - EdgePtr& edge = iter(); - vertices.emplace_back(edge->getVertex()); + template + inline T + FaceT::getArea() noexcept + { + return (m_area); } - return vertices; -} + template + inline T + FaceT::getArea() const noexcept + { + return (m_area); + } -template -inline std::vector> -FaceT::getAllVertexCoordinates() const noexcept -{ - std::vector ret; + template + inline std::shared_ptr>& + FaceT::getHalfEdge() noexcept + { + return (m_halfEdge); + } - for (EdgeIterator iter(*this); iter.ok(); ++iter) { - EdgePtr& edge = iter(); - ret.emplace_back(edge->getVertex()->getPosition()); + template + inline const std::shared_ptr>& + FaceT::getHalfEdge() const noexcept + { + return (m_halfEdge); } - return ret; -} + template + inline std::vector>> + FaceT::gatherVertices() const noexcept + { + std::vector vertices; + + for (EdgeIterator iter(*this); iter.ok(); ++iter) { + EdgePtr& edge = iter(); + vertices.emplace_back(edge->getVertex()); + } + + return vertices; + } -template -inline Vec3T -FaceT::getSmallestCoordinate() const noexcept -{ - const auto coords = this->getAllVertexCoordinates(); + template + inline std::vector> + FaceT::getAllVertexCoordinates() const noexcept + { + std::vector ret; - auto minCoord = coords.front(); + for (EdgeIterator iter(*this); iter.ok(); ++iter) { + EdgePtr& edge = iter(); + ret.emplace_back(edge->getVertex()->getPosition()); + } - for (const auto& c : coords) { - minCoord = min(minCoord, c); + return ret; } - return minCoord; -} + template + inline Vec3T + FaceT::getSmallestCoordinate() const noexcept + { + const auto coords = this->getAllVertexCoordinates(); -template -inline Vec3T -FaceT::getHighestCoordinate() const noexcept -{ - const auto coords = this->getAllVertexCoordinates(); + auto minCoord = coords.front(); - auto maxCoord = coords.front(); + for (const auto& c : coords) { + minCoord = min(minCoord, c); + } - for (const auto& c : coords) { - maxCoord = max(maxCoord, c); + return minCoord; } - return maxCoord; -} - -template -inline Vec3T -FaceT::projectPointIntoFacePlane(const Vec3& a_p) const noexcept -{ - return a_p - m_normal * (m_normal.dot(a_p - m_centroid)); -} - -template -inline bool -FaceT::isPointInsideFace(const Vec3& a_p) const noexcept -{ - const Vec3 p = this->projectPointIntoFacePlane(a_p); - - return m_poly2->isPointInside(p, m_poly2Algorithm); -} - -template -inline T -FaceT::signedDistance(const Vec3& a_x0) const noexcept -{ - T retval = std::numeric_limits::infinity(); - - const bool inside = this->isPointInsideFace(a_x0); - - if (inside) { // Projects to inside so distance and sign are straightforward - // to compute. - retval = m_normal.dot(a_x0 - m_centroid); - } else { - for (const auto& e : m_edges) { // Projects to outside so edge/vertex are - // closest. Check that distance. - const T curDist = e->signedDistance(a_x0); - - retval = (std::abs(curDist) < std::abs(retval)) ? curDist : retval; + template + inline Vec3T + FaceT::getHighestCoordinate() const noexcept + { + const auto coords = this->getAllVertexCoordinates(); + + auto maxCoord = coords.front(); + + for (const auto& c : coords) { + maxCoord = max(maxCoord, c); } + + return maxCoord; + } + + template + inline Vec3T + FaceT::projectPointIntoFacePlane(const Vec3& a_p) const noexcept + { + return a_p - m_normal * (m_normal.dot(a_p - m_centroid)); } - return retval; -} + template + inline bool + FaceT::isPointInsideFace(const Vec3& a_p) const noexcept + { + const Vec3 p = this->projectPointIntoFacePlane(a_p); -template -inline T -FaceT::unsignedDistance2(const Vec3& a_x0) const noexcept -{ - T retval = std::numeric_limits::infinity(); + return m_poly2->isPointInside(p, m_poly2Algorithm); + } - const bool inside = this->isPointInsideFace(a_x0); + template + inline T + FaceT::signedDistance(const Vec3& a_x0) const noexcept + { + T retval = std::numeric_limits::infinity(); - if (inside) { // Projects to inside the polygon face so distance is - // straightforward. - const T normDist = m_normal.dot(a_x0 - m_centroid); + const bool inside = this->isPointInsideFace(a_x0); - retval = normDist * normDist; - } else { - for (const auto& e : m_edges) { // Projects to outside so edge/vertex are closest. - const T curDist2 = e->unsignedDistance2(a_x0); + if (inside) { // Projects to inside so distance and sign are straightforward + // to compute. + retval = m_normal.dot(a_x0 - m_centroid); + } + else { + for (const auto& e : m_edges) { // Projects to outside so edge/vertex are + // closest. Check that distance. + const T curDist = e->signedDistance(a_x0); - retval = (curDist2 < retval) ? curDist2 : retval; + retval = (std::abs(curDist) < std::abs(retval)) ? curDist : retval; + } } + + return retval; } - return retval; -} + template + inline T + FaceT::unsignedDistance2(const Vec3& a_x0) const noexcept + { + T retval = std::numeric_limits::infinity(); + + const bool inside = this->isPointInsideFace(a_x0); + + if (inside) { // Projects to inside the polygon face so distance is + // straightforward. + const T normDist = m_normal.dot(a_x0 - m_centroid); + + retval = normDist * normDist; + } + else { + for (const auto& e : m_edges) { // Projects to outside so edge/vertex are closest. + const T curDist2 = e->unsignedDistance2(a_x0); + + retval = (curDist2 < retval) ? curDist2 : retval; + } + } + + return retval; + } } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_Iterator.hpp b/Source/EBGeometry_DCEL_Iterator.hpp index f50e125e..d31e34f9 100644 --- a/Source/EBGeometry_DCEL_Iterator.hpp +++ b/Source/EBGeometry_DCEL_Iterator.hpp @@ -20,160 +20,160 @@ namespace DCEL { -// Forward declare classes. -template -class VertexT; -template -class EdgeT; -template -class FaceT; - -/*! - @brief Class which can iterate through edges and vertices around a DCEL - polygon face. - @details This class can be used so that it either visits all the half-edges in - a face, or all the outgoing half-edges from a vertex. -*/ -template -class EdgeIteratorT -{ -public: - /*! - @brief Alias to cut down on typing - */ - using Vertex = VertexT; - - /*! - @brief Alias to cut down on typing - */ - using Edge = EdgeT; - - /*! - @brief Alias to cut down on typing - */ - using Face = FaceT; - - /*! - @brief Alias to cut down on typing - */ - using VertexPtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing - */ - using EdgePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing - */ - using FacePtr = std::shared_ptr; - - /*! - @brief Default construction is not allowed. Use one of the full constructors - */ - EdgeIteratorT() = delete; - - /*! - @brief Constructor, taking a face as argument. The iterator begins at the - half-edge pointer contained in the face - @param[in] a_face DCEL polygon face - @note This constructor will will iterate through the half-edges in the - polygon face. - */ - EdgeIteratorT(Face& a_face); + // Forward declare classes. + template + class VertexT; + template + class EdgeT; + template + class FaceT; /*! - @brief Constructor, taking a face as argument. The iterator begins at the - half-edge pointer contained in the face - @param[in] a_face DCEL polygon face - @note This constructor will will iterate through the half-edges in the + @brief Class which can iterate through edges and vertices around a DCEL polygon face. + @details This class can be used so that it either visits all the half-edges in + a face, or all the outgoing half-edges from a vertex. */ - EdgeIteratorT(const Face& a_face); - - /*! - @brief Constructor, taking a vertex as argument. The iterator begins at the - outgoing half-edge from the vertex - @param[in] a_vertex DCEL vertex - @note This constructor will will iterate through the outgoing half-edges - from a vertex. - */ - EdgeIteratorT(Vertex& a_vertex); - - /*! - @brief Constructor, taking a vertex as argument. The iterator begins at the - outgoing half-edge from the vertex - @param[in] a_vertex DCEL vertex - @note This constructor will will iterate through the outgoing half-edges - from a vertex. - */ - EdgeIteratorT(const Vertex& a_vertex); - - /*! - @brief Operator returning a pointer to the current half-edge - */ - inline EdgePtr& - operator()() noexcept; - - /*! - @brief Operator returning a pointer to the current half-edge - */ - inline const EdgePtr& - operator()() const noexcept; - - /*! - @brief Reset function for the iterator. This resets the iterator so that it - begins from the starting half-edge - */ - inline void - reset() noexcept; - - /*! - @brief Incrementation operator, bringing the iterator to the next half-edge - */ - inline void - operator++() noexcept; - - /*! - @brief Function which checks if the iteration can be continued. - @return Returns true unless the current half-edge is a nullptr (i.e., a - broken polygon face) OR a full loop has been made around the polygon face - (i.e. all half-edges have been visited) - */ - inline bool - ok() const noexcept; - -protected: - /*! - @brief Iteration mode, used to distinguish between the two constructors - (face- or vertex-based iteration) - */ - enum class IterationMode + template + class EdgeIteratorT { - Vertices, - Faces + public: + /*! + @brief Alias to cut down on typing + */ + using Vertex = VertexT; + + /*! + @brief Alias to cut down on typing + */ + using Edge = EdgeT; + + /*! + @brief Alias to cut down on typing + */ + using Face = FaceT; + + /*! + @brief Alias to cut down on typing + */ + using VertexPtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing + */ + using EdgePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing + */ + using FacePtr = std::shared_ptr; + + /*! + @brief Default construction is not allowed. Use one of the full constructors + */ + EdgeIteratorT() = delete; + + /*! + @brief Constructor, taking a face as argument. The iterator begins at the + half-edge pointer contained in the face + @param[in] a_face DCEL polygon face + @note This constructor will will iterate through the half-edges in the + polygon face. + */ + EdgeIteratorT(Face& a_face); + + /*! + @brief Constructor, taking a face as argument. The iterator begins at the + half-edge pointer contained in the face + @param[in] a_face DCEL polygon face + @note This constructor will will iterate through the half-edges in the + polygon face. + */ + EdgeIteratorT(const Face& a_face); + + /*! + @brief Constructor, taking a vertex as argument. The iterator begins at the + outgoing half-edge from the vertex + @param[in] a_vertex DCEL vertex + @note This constructor will will iterate through the outgoing half-edges + from a vertex. + */ + EdgeIteratorT(Vertex& a_vertex); + + /*! + @brief Constructor, taking a vertex as argument. The iterator begins at the + outgoing half-edge from the vertex + @param[in] a_vertex DCEL vertex + @note This constructor will will iterate through the outgoing half-edges + from a vertex. + */ + EdgeIteratorT(const Vertex& a_vertex); + + /*! + @brief Operator returning a pointer to the current half-edge + */ + inline EdgePtr& + operator()() noexcept; + + /*! + @brief Operator returning a pointer to the current half-edge + */ + inline const EdgePtr& + operator()() const noexcept; + + /*! + @brief Reset function for the iterator. This resets the iterator so that it + begins from the starting half-edge + */ + inline void + reset() noexcept; + + /*! + @brief Incrementation operator, bringing the iterator to the next half-edge + */ + inline void + operator++() noexcept; + + /*! + @brief Function which checks if the iteration can be continued. + @return Returns true unless the current half-edge is a nullptr (i.e., a + broken polygon face) OR a full loop has been made around the polygon face + (i.e. all half-edges have been visited) + */ + inline bool + ok() const noexcept; + + protected: + /*! + @brief Iteration mode, used to distinguish between the two constructors + (face- or vertex-based iteration) + */ + enum class IterationMode + { + Vertices, + Faces + }; + + /*! + @brief If true, a full loop has been made around the polygon face + */ + bool m_fullLoop; + + /*! + @brief Iteration mode. Set in constructor + */ + IterationMode m_iterMode; + + /*! + @brief Starting half-edge + */ + std::shared_ptr m_startEdge; + + /*! + @brief Current half-edge + */ + std::shared_ptr m_curEdge; }; - - /*! - @brief If true, a full loop has been made around the polygon face - */ - bool m_fullLoop; - - /*! - @brief Iteration mode. Set in constructor - */ - IterationMode m_iterMode; - - /*! - @brief Starting half-edge - */ - std::shared_ptr m_startEdge; - - /*! - @brief Current half-edge - */ - std::shared_ptr m_curEdge; -}; } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_IteratorImplem.hpp b/Source/EBGeometry_DCEL_IteratorImplem.hpp index 939961a5..b1b8cb89 100644 --- a/Source/EBGeometry_DCEL_IteratorImplem.hpp +++ b/Source/EBGeometry_DCEL_IteratorImplem.hpp @@ -21,95 +21,95 @@ namespace DCEL { -template -inline EdgeIteratorT::EdgeIteratorT(Face& a_face) -{ - m_startEdge = a_face.getHalfEdge(); - m_curEdge = m_startEdge; - m_fullLoop = false; - m_iterMode = IterationMode::Faces; -} - -template -inline EdgeIteratorT::EdgeIteratorT(const Face& a_face) -{ - m_startEdge = a_face.getHalfEdge(); - m_curEdge = m_startEdge; - m_fullLoop = false; - m_iterMode = IterationMode::Faces; -} - -template -inline EdgeIteratorT::EdgeIteratorT(Vertex& a_vert) -{ - m_startEdge = a_vert.getOutgoingEdge(); - m_curEdge = m_startEdge; - m_fullLoop = false; - m_iterMode = IterationMode::Vertices; -} - -template -inline EdgeIteratorT::EdgeIteratorT(const Vertex& a_vert) -{ - m_startEdge = a_vert.getOutgoingEdge(); - m_curEdge = m_startEdge; - m_fullLoop = false; - m_iterMode = IterationMode::Vertices; -} - -template -inline std::shared_ptr>& -EdgeIteratorT::operator()() noexcept -{ - return (m_curEdge); -} - -template -inline const std::shared_ptr>& -EdgeIteratorT::operator()() const noexcept -{ - return (m_curEdge); -} - -template -inline void -EdgeIteratorT::reset() noexcept -{ - m_curEdge = m_startEdge; - m_fullLoop = false; -} - -template -inline void -EdgeIteratorT::operator++() noexcept -{ - switch (m_iterMode) { - case IterationMode::Faces: { - m_curEdge = m_curEdge->getNextEdge(); - - break; + template + inline EdgeIteratorT::EdgeIteratorT(Face& a_face) + { + m_startEdge = a_face.getHalfEdge(); + m_curEdge = m_startEdge; + m_fullLoop = false; + m_iterMode = IterationMode::Faces; } - case IterationMode::Vertices: { - // For vertices, we want to compute the - m_curEdge = m_curEdge->getPreviousEdge()->getPairEdge(); - break; + template + inline EdgeIteratorT::EdgeIteratorT(const Face& a_face) + { + m_startEdge = a_face.getHalfEdge(); + m_curEdge = m_startEdge; + m_fullLoop = false; + m_iterMode = IterationMode::Faces; } - default: { - std::cerr << "In file 'EBGeometry_DCEL_IteratorImplem.hpp function " - "EdgeIteratorT::operator++ - logic bust\n"; + + template + inline EdgeIteratorT::EdgeIteratorT(Vertex& a_vert) + { + m_startEdge = a_vert.getOutgoingEdge(); + m_curEdge = m_startEdge; + m_fullLoop = false; + m_iterMode = IterationMode::Vertices; + } + + template + inline EdgeIteratorT::EdgeIteratorT(const Vertex& a_vert) + { + m_startEdge = a_vert.getOutgoingEdge(); + m_curEdge = m_startEdge; + m_fullLoop = false; + m_iterMode = IterationMode::Vertices; + } + + template + inline std::shared_ptr>& + EdgeIteratorT::operator()() noexcept + { + return (m_curEdge); } + + template + inline const std::shared_ptr>& + EdgeIteratorT::operator()() const noexcept + { + return (m_curEdge); } - m_fullLoop = (m_curEdge == m_startEdge); -} + template + inline void + EdgeIteratorT::reset() noexcept + { + m_curEdge = m_startEdge; + m_fullLoop = false; + } -template -inline bool -EdgeIteratorT::ok() const noexcept -{ - return !m_fullLoop && m_curEdge; -} + template + inline void + EdgeIteratorT::operator++() noexcept + { + switch (m_iterMode) { + case IterationMode::Faces: { + m_curEdge = m_curEdge->getNextEdge(); + + break; + } + case IterationMode::Vertices: { + // For vertices, we want to compute the + m_curEdge = m_curEdge->getPreviousEdge()->getPairEdge(); + + break; + } + default: { + std::cerr << "In file 'EBGeometry_DCEL_IteratorImplem.hpp function " + "EdgeIteratorT::operator++ - logic bust\n"; + } + } + + m_fullLoop = (m_curEdge == m_startEdge); + } + + template + inline bool + EdgeIteratorT::ok() const noexcept + { + return !m_fullLoop && m_curEdge; + } } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_Mesh.hpp b/Source/EBGeometry_DCEL_Mesh.hpp index 2afcfee6..3e43e856 100644 --- a/Source/EBGeometry_DCEL_Mesh.hpp +++ b/Source/EBGeometry_DCEL_Mesh.hpp @@ -28,332 +28,332 @@ class Polygon2D; namespace DCEL { -// Forward declare classes. -template -class VertexT; -template -class EdgeT; -template -class FaceT; - -/*! - @brief Mesh class which stores a full DCEL mesh (with signed distance - functions) - @details This encapsulates a full DCEL mesh, and also includes DIRECT signed - distance functions. The mesh consists of a set of vertices, half-edges, and - polygon faces who each have references to other vertices, half-edges, and - polygon faces. The signed distance functions DIRECT, which means that they go - through ALL of the polygon faces and compute the signed distance to them. This - is extremely inefficient, which is why this class is almost always embedded - into a bounding volume hierarchy. - @note This class is not for the light of heart -- it will almost always be - instantiated through a file parser which reads vertices and edges from file - and builds the mesh from that. Do not try to build a MeshT object yourself, - use file parsers! -*/ -template -class MeshT : public SignedDistanceFunction -{ -public: - /*! - @brief Possible search algorithms for DCEL::MeshT - @details Direct means compute the signed distance for all primitives, - Direct2 means compute the squared signed distance for all primitives. - */ - enum class SearchAlgorithm - { - Direct, - Direct2, - }; - - /*! - @brief How to weight vertex normal - */ - enum class VertexNormalWeight + // Forward declare classes. + template + class VertexT; + template + class EdgeT; + template + class FaceT; + + /*! + @brief Mesh class which stores a full DCEL mesh (with signed distance + functions) + @details This encapsulates a full DCEL mesh, and also includes DIRECT signed + distance functions. The mesh consists of a set of vertices, half-edges, and + polygon faces who each have references to other vertices, half-edges, and + polygon faces. The signed distance functions DIRECT, which means that they go + through ALL of the polygon faces and compute the signed distance to them. This + is extremely inefficient, which is why this class is almost always embedded + into a bounding volume hierarchy. + @note This class is not for the light of heart -- it will almost always be + instantiated through a file parser which reads vertices and edges from file + and builds the mesh from that. Do not try to build a MeshT object yourself, + use file parsers! + */ + template + class MeshT : public SignedDistanceFunction { - None, - Angle, + public: + /*! + @brief Possible search algorithms for DCEL::MeshT + @details Direct means compute the signed distance for all primitives, + Direct2 means compute the squared signed distance for all primitives. + */ + enum class SearchAlgorithm + { + Direct, + Direct2, + }; + + /*! + @brief How to weight vertex normal + */ + enum class VertexNormalWeight + { + None, + Angle, + }; + + /*! + @brief Alias to cut down on the typing + */ + using Vec3 = Vec3T; + + /*! + @brief Alias to cut down on the typing + */ + using Vertex = VertexT; + + /*! + @brief Alias to cut down on the typing + */ + using Edge = EdgeT; + + /*! + @brief Alias to cut down on the typing + */ + using Face = FaceT; + + /*! + @brief Alias to cut down on the typing + */ + using VertexPtr = std::shared_ptr; + + /*! + @brief Alias to cut down on the typing + */ + using EdgePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on the typing + */ + using FacePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on the typing + */ + using Mesh = MeshT; + + /*! + @brief Default constructor. Leaves unobject in an unusable state + */ + MeshT(); + + /*! + @brief Disallowed copy construction + @param[in] a_otherMesh Other mesh + */ + MeshT(const Mesh& a_otherMesh) = delete; + + /*! + @brief Full constructor. This provides the faces, edges, and vertices to the + mesh. + @details Calls define(a_faces, a_edges, a_vertices) + @param[in] a_faces Polygon faces + @param[in] a_edges Half-edges + @param[in] a_vertices Vertices + @note The constructor arguments should provide a complete DCEL mesh + description. This is usually done through a file parser which reads a mesh + file format and creates the DCEL mesh structure + */ + MeshT(std::vector& a_faces, std::vector& a_edges, std::vector& a_vertices); + + /*! + @brief Destructor (does nothing) + */ + ~MeshT(); + + /*! + @brief Define function. Puts Mesh in usable state. + @param[in] a_faces Polygon faces + @param[in] a_edges Half-edges + @param[in] a_vertices Vertices + @note The function arguments should provide a complete DCEL mesh + description. This is usually done through a file parser which reads a mesh + file format and creates the DCEL mesh structure. Note that this only + involves associating pointer structures through the mesh. Internal + parameters like face area and normal is computed in MeshT::reconcile. + */ + inline void + define(std::vector& a_faces, std::vector& a_edges, std::vector& a_vertices) noexcept; + + /*! + @brief Perform a sanity check. + @details This will provide error messages if vertices are badly linked, + faces are nullptr, and so on. These messages are logged by calling + incrementWarning() which identifies types of errors that can occur, and how + many of those errors have occurred. + */ + inline void + sanityCheck() const noexcept; + + /*! + @brief Search algorithm for direct signed distance computations + @param[in] a_algorithm Algorithm to use + */ + inline void + setSearchAlgorithm(const SearchAlgorithm a_algorithm) noexcept; + + /*! + @brief Set the inside/outside algorithm to use when computing the signed + distance to polygon faces. + @details Computing the signed distance to faces requires testing if a point + projected to a polygo face plane falls inside or outside the polygon face. + There are multiple algorithms to use here. + @param[in] a_algorithm Algorithm to use + */ + inline void + setInsideOutsideAlgorithm(typename Polygon2D::InsideOutsideAlgorithm a_algorithm) noexcept; + + /*! + @brief Reconcile function which computes the internal parameters in + vertices, edges, and faces for use with signed distance functionality + @param[in] a_weight Vertex angle weighting function. Either + VertexNormalWeight::None for unweighted vertex normals or + VertexNormalWeight::Angle for the pseudonormal + @details This will reconcile faces, edges, and vertices, e.g. computing the + area and normal vector for faces + */ + inline void + reconcile(typename DCEL::MeshT::VertexNormalWeight a_weight = VertexNormalWeight::Angle) noexcept; + + /*! + @brief Get modifiable vertices in this mesh + */ + inline std::vector& + getVertices() noexcept; + + /*! + @brief Get immutable vertices in this mesh + */ + inline const std::vector& + getVertices() const noexcept; + + /*! + @brief Get modifiable half-edges in this mesh + */ + inline std::vector& + getEdges() noexcept; + + /*! + @brief Get immutable half-edges in this mesh + */ + inline const std::vector& + getEdges() const noexcept; + + /*! + @brief Get modifiable faces in this mesh + */ + inline std::vector& + getFaces() noexcept; + + /*! + @brief Get immutable faces in this mesh + */ + inline const std::vector& + getFaces() const noexcept; + + /*! + @brief Compute the signed distance from a point to this mesh + @param[in] a_x0 3D point in space. + @details This function will iterate through ALL faces in the mesh and return + the value with the smallest magnitude. This is horrendously slow, which is + why this function is almost never called. Rather, MeshT can be embedded + in a bounding volume hierarchy for faster access. + @note This will call the other version with the object's search algorithm. + */ + inline T + signedDistance(const Vec3& a_x0) const noexcept override; + + /*! + @brief Compute the signed distance from a point to this mesh + @param[in] a_x0 3D point in space. + @param[in] a_algorithm Search algorithm + @details This function will iterate through ALL faces in the mesh and return + the value with the smallest magnitude. This is horrendously slow, which is + why this function is almost never called. Rather, MeshT can be embedded + in a bounding volume hierarchy for faster access. + */ + inline T + signedDistance(const Vec3& a_x0, SearchAlgorithm a_algorithm) const noexcept; + + /*! + @brief Compute the unsigned square distance from a point to this mesh + @param[in] a_x0 3D point in space. + @details This function will iterate through ALL faces in the mesh and return + the value with the smallest magnitude. This is horrendously slow, which is + why this function is almost never called. Rather, MeshT can be embedded + in a bounding volume hierarchy for faster access. + @note This will call the other version with the object's search algorithm. + */ + inline T + unsignedDistance2(const Vec3& a_x0) const noexcept; + + protected: + /*! + @brief Search algorithm. Only used in signed distance functions. + */ + SearchAlgorithm m_algorithm; + + /*! + @brief Mesh vertices + */ + std::vector m_vertices; + + /*! + @brief Mesh half-edges + */ + std::vector m_edges; + + /*! + @brief Mesh faces + */ + std::vector m_faces; + + /*! + @brief Return all vertex coordinates in the mesh. + */ + inline std::vector> + getAllVertexCoordinates() const noexcept; + + /*! + @brief Function which computes internal things for the polygon faces. + @note This calls DCEL::FaceT::reconcile() + */ + inline void + reconcileFaces() noexcept; + + /*! + @brief Function which computes internal things for the half-edges + @note This calls DCEL::EdgeT::reconcile() + */ + inline void + reconcileEdges() noexcept; + + /*! + @brief Function which computes internal things for the vertices + @param[in] a_weight Vertex angle weighting + @note This calls DCEL::VertexT::computeVertexNormalAverage() or + DCEL::VertexT::computeVertexNormalAngleWeighted() + */ + inline void + reconcileVertices(typename DCEL::MeshT::VertexNormalWeight a_weight) noexcept; + + /*! + @brief Implementation of signed distance function which iterates through all + faces + @param[in] a_point 3D point + */ + inline T + DirectSignedDistance(const Vec3& a_point) const noexcept; + + /*! + @brief Implementation of squared signed distance function which iterates + through all faces. + @details This first find the face with the smallest unsigned square + distance, and the returns the signed distance to that face (more efficient + than the other version). + @param[in] a_point 3D point + */ + inline T + DirectSignedDistance2(const Vec3& a_point) const noexcept; + + /*! + @brief Increment a warning. This is used in sanityCheck() for locating holes + or bad inputs in the mesh. + @param[in] a_warnings Map of all registered warnings + @param[in] a_warn Current warning to increment by + */ + inline void + incrementWarning(std::map& a_warnings, const std::string& a_warn) const noexcept; + + /*! + @brief Print all warnings to std::cerr + */ + inline void + printWarnings(const std::map& a_warnings) const noexcept; }; - - /*! - @brief Alias to cut down on the typing - */ - using Vec3 = Vec3T; - - /*! - @brief Alias to cut down on the typing - */ - using Vertex = VertexT; - - /*! - @brief Alias to cut down on the typing - */ - using Edge = EdgeT; - - /*! - @brief Alias to cut down on the typing - */ - using Face = FaceT; - - /*! - @brief Alias to cut down on the typing - */ - using VertexPtr = std::shared_ptr; - - /*! - @brief Alias to cut down on the typing - */ - using EdgePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on the typing - */ - using FacePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on the typing - */ - using Mesh = MeshT; - - /*! - @brief Default constructor. Leaves unobject in an unusable state - */ - MeshT(); - - /*! - @brief Disallowed copy construction - @param[in] a_otherMesh Other mesh - */ - MeshT(const Mesh& a_otherMesh) = delete; - - /*! - @brief Full constructor. This provides the faces, edges, and vertices to the - mesh. - @details Calls define(a_faces, a_edges, a_vertices) - @param[in] a_faces Polygon faces - @param[in] a_edges Half-edges - @param[in] a_vertices Vertices - @note The constructor arguments should provide a complete DCEL mesh - description. This is usually done through a file parser which reads a mesh - file format and creates the DCEL mesh structure - */ - MeshT(std::vector& a_faces, std::vector& a_edges, std::vector& a_vertices); - - /*! - @brief Destructor (does nothing) - */ - ~MeshT(); - - /*! - @brief Define function. Puts Mesh in usable state. - @param[in] a_faces Polygon faces - @param[in] a_edges Half-edges - @param[in] a_vertices Vertices - @note The function arguments should provide a complete DCEL mesh - description. This is usually done through a file parser which reads a mesh - file format and creates the DCEL mesh structure. Note that this only - involves associating pointer structures through the mesh. Internal - parameters like face area and normal is computed in MeshT::reconcile. - */ - inline void - define(std::vector& a_faces, std::vector& a_edges, std::vector& a_vertices) noexcept; - - /*! - @brief Perform a sanity check. - @details This will provide error messages if vertices are badly linked, - faces are nullptr, and so on. These messages are logged by calling - incrementWarning() which identifies types of errors that can occur, and how - many of those errors have occurred. - */ - inline void - sanityCheck() const noexcept; - - /*! - @brief Search algorithm for direct signed distance computations - @param[in] a_algorithm Algorithm to use - */ - inline void - setSearchAlgorithm(const SearchAlgorithm a_algorithm) noexcept; - - /*! - @brief Set the inside/outside algorithm to use when computing the signed - distance to polygon faces. - @details Computing the signed distance to faces requires testing if a point - projected to a polygo face plane falls inside or outside the polygon face. - There are multiple algorithms to use here. - @param[in] a_algorithm Algorithm to use - */ - inline void - setInsideOutsideAlgorithm(typename Polygon2D::InsideOutsideAlgorithm a_algorithm) noexcept; - - /*! - @brief Reconcile function which computes the internal parameters in - vertices, edges, and faces for use with signed distance functionality - @param[in] a_weight Vertex angle weighting function. Either - VertexNormalWeight::None for unweighted vertex normals or - VertexNormalWeight::Angle for the pseudonormal - @details This will reconcile faces, edges, and vertices, e.g. computing the - area and normal vector for faces - */ - inline void - reconcile(typename DCEL::MeshT::VertexNormalWeight a_weight = VertexNormalWeight::Angle) noexcept; - - /*! - @brief Get modifiable vertices in this mesh - */ - inline std::vector& - getVertices() noexcept; - - /*! - @brief Get immutable vertices in this mesh - */ - inline const std::vector& - getVertices() const noexcept; - - /*! - @brief Get modifiable half-edges in this mesh - */ - inline std::vector& - getEdges() noexcept; - - /*! - @brief Get immutable half-edges in this mesh - */ - inline const std::vector& - getEdges() const noexcept; - - /*! - @brief Get modifiable faces in this mesh - */ - inline std::vector& - getFaces() noexcept; - - /*! - @brief Get immutable faces in this mesh - */ - inline const std::vector& - getFaces() const noexcept; - - /*! - @brief Compute the signed distance from a point to this mesh - @param[in] a_x0 3D point in space. - @details This function will iterate through ALL faces in the mesh and return - the value with the smallest magnitude. This is horrendously slow, which is - why this function is almost never called. Rather, MeshT can be embedded - in a bounding volume hierarchy for faster access. - @note This will call the other version with the object's search algorithm. - */ - inline T - signedDistance(const Vec3& a_x0) const noexcept override; - - /*! - @brief Compute the signed distance from a point to this mesh - @param[in] a_x0 3D point in space. - @param[in] a_algorithm Search algorithm - @details This function will iterate through ALL faces in the mesh and return - the value with the smallest magnitude. This is horrendously slow, which is - why this function is almost never called. Rather, MeshT can be embedded - in a bounding volume hierarchy for faster access. - */ - inline T - signedDistance(const Vec3& a_x0, SearchAlgorithm a_algorithm) const noexcept; - - /*! - @brief Compute the unsigned square distance from a point to this mesh - @param[in] a_x0 3D point in space. - @details This function will iterate through ALL faces in the mesh and return - the value with the smallest magnitude. This is horrendously slow, which is - why this function is almost never called. Rather, MeshT can be embedded - in a bounding volume hierarchy for faster access. - @note This will call the other version with the object's search algorithm. - */ - inline T - unsignedDistance2(const Vec3& a_x0) const noexcept; - -protected: - /*! - @brief Search algorithm. Only used in signed distance functions. - */ - SearchAlgorithm m_algorithm; - - /*! - @brief Mesh vertices - */ - std::vector m_vertices; - - /*! - @brief Mesh half-edges - */ - std::vector m_edges; - - /*! - @brief Mesh faces - */ - std::vector m_faces; - - /*! - @brief Return all vertex coordinates in the mesh. - */ - inline std::vector> - getAllVertexCoordinates() const noexcept; - - /*! - @brief Function which computes internal things for the polygon faces. - @note This calls DCEL::FaceT::reconcile() - */ - inline void - reconcileFaces() noexcept; - - /*! - @brief Function which computes internal things for the half-edges - @note This calls DCEL::EdgeT::reconcile() - */ - inline void - reconcileEdges() noexcept; - - /*! - @brief Function which computes internal things for the vertices - @param[in] a_weight Vertex angle weighting - @note This calls DCEL::VertexT::computeVertexNormalAverage() or - DCEL::VertexT::computeVertexNormalAngleWeighted() - */ - inline void - reconcileVertices(typename DCEL::MeshT::VertexNormalWeight a_weight) noexcept; - - /*! - @brief Implementation of signed distance function which iterates through all - faces - @param[in] a_point 3D point - */ - inline T - DirectSignedDistance(const Vec3& a_point) const noexcept; - - /*! - @brief Implementation of squared signed distance function which iterates - through all faces. - @details This first find the face with the smallest unsigned square - distance, and the returns the signed distance to that face (more efficient - than the other version). - @param[in] a_point 3D point - */ - inline T - DirectSignedDistance2(const Vec3& a_point) const noexcept; - - /*! - @brief Increment a warning. This is used in sanityCheck() for locating holes - or bad inputs in the mesh. - @param[in] a_warnings Map of all registered warnings - @param[in] a_warn Current warning to increment by - */ - inline void - incrementWarning(std::map& a_warnings, const std::string& a_warn) const noexcept; - - /*! - @brief Print all warnings to std::cerr - */ - inline void - printWarnings(const std::map& a_warnings) const noexcept; -}; } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_MeshImplem.hpp b/Source/EBGeometry_DCEL_MeshImplem.hpp index c0cbb612..37d50487 100644 --- a/Source/EBGeometry_DCEL_MeshImplem.hpp +++ b/Source/EBGeometry_DCEL_MeshImplem.hpp @@ -22,349 +22,370 @@ namespace DCEL { -template -inline MeshT::MeshT() -{ - m_algorithm = SearchAlgorithm::Direct2; -} - -template -inline MeshT::MeshT(std::vector& a_faces, std::vector& a_edges, std::vector& a_vertices) - : MeshT() -{ - this->define(a_faces, a_edges, a_vertices); -} - -template -inline MeshT::~MeshT() -{ -} - -template -inline void -MeshT::define( - std::vector& a_faces, std::vector& a_edges, std::vector& a_vertices) noexcept -{ - m_faces = a_faces; - m_edges = a_edges; - m_vertices = a_vertices; -} - -template -inline void -MeshT::incrementWarning(std::map& a_warnings, const std::string& a_warn) const noexcept -{ - a_warnings.at(a_warn) += 1; -} - -template -inline void -MeshT::printWarnings(const std::map& a_warnings) const noexcept -{ - for (const auto& warn : a_warnings) { - if (warn.second > 0) { - std::cerr << "In file 'CD_DCELMeshImplem.H' function " - "MeshT::sanityCheck() - warnings about error '" - << warn.first << "' = " << warn.second << "\n"; - } + template + inline MeshT::MeshT() + { + m_algorithm = SearchAlgorithm::Direct2; } -} - -template -inline void -MeshT::sanityCheck() const noexcept -{ - - const std::string f_null = "nullptr face"; - const std::string f_noEdge = "face with no edge"; - const std::string f_degenerate = "degenerate face"; - - const std::string e_null = "nullptr edges"; - const std::string e_degenerate = "degenerate edge"; - const std::string e_noPairEdge = "no pair edge (not watertight)"; - const std::string e_noNextEdge = "no next edge (badly linked dcel)"; - const std::string e_noPrevEdge = "no previous edge (badly linked dcel)"; - const std::string e_noOrigVert = "no origin vertex found for half edge (badly linked dcel)"; - const std::string e_noFace = "no face found for half edge (badly linked dcel)"; - const std::string e_noPrevNext = "previous edge's next edge is not this edge (badly linked dcel)"; - const std::string e_noNextPrev = "next edge's previous edge is not this edge (badly linked dcel)"; - - const std::string v_null = "nullptr vertex"; - const std::string v_noEdge = "no referenced edge for vertex (unreferenced vertex)"; - - std::map warnings = {{f_null, 0}, {f_noEdge, 0}, {f_degenerate, 0}, {e_null, 0}, - {e_degenerate, 0}, {e_noPairEdge, 0}, {e_noNextEdge, 0}, {e_noPrevEdge, 0}, - {e_noOrigVert, 0}, {e_noFace, 0}, {e_noPrevNext, 0}, {e_noNextPrev, 0}, - {v_null, 0}, {v_noEdge, 0}}; - - for (const auto& f : m_faces) { - const auto& halfEdge = f->getHalfEdge(); - - // Check for duplicate vertices - auto vertices = f->gatherVertices(); - std::sort(vertices.begin(), vertices.end()); - auto it = std::unique(vertices.begin(), vertices.end()); - const bool noDuplicates = (it == vertices.end()); - - if (f == nullptr) { - incrementWarning(warnings, f_null); - } else if (halfEdge == nullptr) { - incrementWarning(warnings, f_noEdge); - } - if (!noDuplicates) { - incrementWarning(warnings, f_degenerate); + + template + inline MeshT::MeshT(std::vector& a_faces, + std::vector& a_edges, + std::vector& a_vertices) + : MeshT() + { + this->define(a_faces, a_edges, a_vertices); + } + + template + inline MeshT::~MeshT() + {} + + template + inline void + MeshT::define(std::vector& a_faces, + std::vector& a_edges, + std::vector& a_vertices) noexcept + { + m_faces = a_faces; + m_edges = a_edges; + m_vertices = a_vertices; + } + + template + inline void + MeshT::incrementWarning(std::map& a_warnings, const std::string& a_warn) const noexcept + { + a_warnings.at(a_warn) += 1; + } + + template + inline void + MeshT::printWarnings(const std::map& a_warnings) const noexcept + { + for (const auto& warn : a_warnings) { + if (warn.second > 0) { + std::cerr << "In file 'CD_DCELMeshImplem.H' function " + "MeshT::sanityCheck() - warnings about error '" + << warn.first << "' = " << warn.second << "\n"; + } } } - for (const auto& e : m_edges) { - const auto& nextEdge = e->getNextEdge(); - const auto& prevEdge = e->getPreviousEdge(); - const auto& pairEdge = e->getPairEdge(); - const auto& curVertex = e->getVertex(); - const auto& curFace = e->getFace(); - - // Check basic points for current edge. - if (e == nullptr) { - incrementWarning(warnings, e_null); - } else if (e->getVertex() == e->getOtherVertex()) { - incrementWarning(warnings, e_degenerate); - } else if (pairEdge == nullptr) { - incrementWarning(warnings, e_noPairEdge); - } else if (nextEdge == nullptr) { - incrementWarning(warnings, e_noNextEdge); - } else if (prevEdge == nullptr) { - incrementWarning(warnings, e_noPrevEdge); - } else if (curVertex == nullptr) { - incrementWarning(warnings, e_noOrigVert); - } else if (curFace == nullptr) { - incrementWarning(warnings, e_noFace); + template + inline void + MeshT::sanityCheck() const noexcept + { + + const std::string f_null = "nullptr face"; + const std::string f_noEdge = "face with no edge"; + const std::string f_degenerate = "degenerate face"; + + const std::string e_null = "nullptr edges"; + const std::string e_degenerate = "degenerate edge"; + const std::string e_noPairEdge = "no pair edge (not watertight)"; + const std::string e_noNextEdge = "no next edge (badly linked dcel)"; + const std::string e_noPrevEdge = "no previous edge (badly linked dcel)"; + const std::string e_noOrigVert = "no origin vertex found for half edge (badly linked dcel)"; + const std::string e_noFace = "no face found for half edge (badly linked dcel)"; + const std::string e_noPrevNext = "previous edge's next edge is not this edge (badly linked dcel)"; + const std::string e_noNextPrev = "next edge's previous edge is not this edge (badly linked dcel)"; + + const std::string v_null = "nullptr vertex"; + const std::string v_noEdge = "no referenced edge for vertex (unreferenced vertex)"; + + std::map warnings = {{f_null, 0}, + {f_noEdge, 0}, + {f_degenerate, 0}, + {e_null, 0}, + {e_degenerate, 0}, + {e_noPairEdge, 0}, + {e_noNextEdge, 0}, + {e_noPrevEdge, 0}, + {e_noOrigVert, 0}, + {e_noFace, 0}, + {e_noPrevNext, 0}, + {e_noNextPrev, 0}, + {v_null, 0}, + {v_noEdge, 0}}; + + for (const auto& f : m_faces) { + const auto& halfEdge = f->getHalfEdge(); + + // Check for duplicate vertices + auto vertices = f->gatherVertices(); + std::sort(vertices.begin(), vertices.end()); + auto it = std::unique(vertices.begin(), vertices.end()); + const bool noDuplicates = (it == vertices.end()); + + if (f == nullptr) { + incrementWarning(warnings, f_null); + } + else if (halfEdge == nullptr) { + incrementWarning(warnings, f_noEdge); + } + if (!noDuplicates) { + incrementWarning(warnings, f_degenerate); + } } - // Check that the next edge's previous edge is this edge. - if (prevEdge->getNextEdge() != e) { - incrementWarning(warnings, e_noPrevNext); - } else if (nextEdge->getPreviousEdge() != e) { - incrementWarning(warnings, e_noNextPrev); + for (const auto& e : m_edges) { + const auto& nextEdge = e->getNextEdge(); + const auto& prevEdge = e->getPreviousEdge(); + const auto& pairEdge = e->getPairEdge(); + const auto& curVertex = e->getVertex(); + const auto& curFace = e->getFace(); + + // Check basic points for current edge. + if (e == nullptr) { + incrementWarning(warnings, e_null); + } + else if (e->getVertex() == e->getOtherVertex()) { + incrementWarning(warnings, e_degenerate); + } + else if (pairEdge == nullptr) { + incrementWarning(warnings, e_noPairEdge); + } + else if (nextEdge == nullptr) { + incrementWarning(warnings, e_noNextEdge); + } + else if (prevEdge == nullptr) { + incrementWarning(warnings, e_noPrevEdge); + } + else if (curVertex == nullptr) { + incrementWarning(warnings, e_noOrigVert); + } + else if (curFace == nullptr) { + incrementWarning(warnings, e_noFace); + } + + // Check that the next edge's previous edge is this edge. + if (prevEdge->getNextEdge() != e) { + incrementWarning(warnings, e_noPrevNext); + } + else if (nextEdge->getPreviousEdge() != e) { + incrementWarning(warnings, e_noNextPrev); + } } - } - // Vertex check - for (const auto& v : m_vertices) { - if (v == nullptr) { - incrementWarning(warnings, v_null); - } else if (v->getOutgoingEdge() == nullptr) { - incrementWarning(warnings, v_noEdge); + // Vertex check + for (const auto& v : m_vertices) { + if (v == nullptr) { + incrementWarning(warnings, v_null); + } + else if (v->getOutgoingEdge() == nullptr) { + incrementWarning(warnings, v_noEdge); + } } + + this->printWarnings(warnings); } - this->printWarnings(warnings); -} - -template -inline void -MeshT::setSearchAlgorithm(const SearchAlgorithm a_algorithm) noexcept -{ - m_algorithm = a_algorithm; -} - -template -inline void -MeshT::setInsideOutsideAlgorithm(typename Polygon2D::InsideOutsideAlgorithm a_algorithm) noexcept -{ - for (auto& f : m_faces) { - f->setInsideOutsideAlgorithm(a_algorithm); + template + inline void + MeshT::setSearchAlgorithm(const SearchAlgorithm a_algorithm) noexcept + { + m_algorithm = a_algorithm; } -} - -template -inline void -MeshT::reconcile(typename DCEL::MeshT::VertexNormalWeight a_weight) noexcept -{ - this->reconcileFaces(); - this->reconcileEdges(); - this->reconcileVertices(a_weight); -} - -template -inline void -MeshT::reconcileFaces() noexcept -{ - for (auto& f : m_faces) { - f->reconcile(); + + template + inline void + MeshT::setInsideOutsideAlgorithm(typename Polygon2D::InsideOutsideAlgorithm a_algorithm) noexcept + { + for (auto& f : m_faces) { + f->setInsideOutsideAlgorithm(a_algorithm); + } } -} - -template -inline void -MeshT::reconcileEdges() noexcept -{ - for (auto& e : m_edges) { - e->reconcile(); + + template + inline void + MeshT::reconcile(typename DCEL::MeshT::VertexNormalWeight a_weight) noexcept + { + this->reconcileFaces(); + this->reconcileEdges(); + this->reconcileVertices(a_weight); } -} - -template -inline void -MeshT::reconcileVertices(typename DCEL::MeshT::VertexNormalWeight a_weight) noexcept -{ - for (auto& v : m_vertices) { - switch (a_weight) { - case VertexNormalWeight::None: - v->computeVertexNormalAverage(); - break; - case VertexNormalWeight::Angle: - v->computeVertexNormalAngleWeighted(); - break; - default: - std::cerr << "In file 'CD_DCELMeshImplem.H' function " - "DCEL::MeshT::reconcileVertices(VertexNormalWeighting) - " - "unsupported algorithm requested\n"; + + template + inline void + MeshT::reconcileFaces() noexcept + { + for (auto& f : m_faces) { + f->reconcile(); } } -} - -template -inline std::vector>>& -MeshT::getVertices() noexcept -{ - return (m_vertices); -} - -template -inline const std::vector>>& -MeshT::getVertices() const noexcept -{ - return (m_vertices); -} - -template -inline std::vector>>& -MeshT::getEdges() noexcept -{ - return (m_edges); -} - -template -inline const std::vector>>& -MeshT::getEdges() const noexcept -{ - return (m_edges); -} - -template -inline std::vector>>& -MeshT::getFaces() noexcept -{ - return (m_faces); -} - -template -inline const std::vector>>& -MeshT::getFaces() const noexcept -{ - return (m_faces); -} - -template -inline std::vector> -MeshT::getAllVertexCoordinates() const noexcept -{ - std::vector vertexCoordinates; - for (const auto& v : m_vertices) { - vertexCoordinates.emplace_back(v->getPosition()); + + template + inline void + MeshT::reconcileEdges() noexcept + { + for (auto& e : m_edges) { + e->reconcile(); + } } - return vertexCoordinates; -} + template + inline void + MeshT::reconcileVertices(typename DCEL::MeshT::VertexNormalWeight a_weight) noexcept + { + for (auto& v : m_vertices) { + switch (a_weight) { + case VertexNormalWeight::None: + v->computeVertexNormalAverage(); + break; + case VertexNormalWeight::Angle: + v->computeVertexNormalAngleWeighted(); + break; + default: + std::cerr << "In file 'CD_DCELMeshImplem.H' function " + "DCEL::MeshT::reconcileVertices(VertexNormalWeighting) - " + "unsupported algorithm requested\n"; + } + } + } -template -inline T -MeshT::signedDistance(const Vec3& a_point) const noexcept -{ - return this->signedDistance(a_point, m_algorithm); -} + template + inline std::vector>>& + MeshT::getVertices() noexcept + { + return (m_vertices); + } -template -inline T -MeshT::unsignedDistance2(const Vec3& a_point) const noexcept -{ - T minDist2 = std::numeric_limits::max(); + template + inline const std::vector>>& + MeshT::getVertices() const noexcept + { + return (m_vertices); + } - for (const auto& f : m_faces) { - const T curDist2 = f->unsignedDistance2(a_point); + template + inline std::vector>>& + MeshT::getEdges() noexcept + { + return (m_edges); + } - minDist2 = std::min(minDist2, curDist2); + template + inline const std::vector>>& + MeshT::getEdges() const noexcept + { + return (m_edges); } - return minDist2; -} + template + inline std::vector>>& + MeshT::getFaces() noexcept + { + return (m_faces); + } -template -inline T -MeshT::signedDistance(const Vec3& a_point, SearchAlgorithm a_algorithm) const noexcept -{ - T minDist = std::numeric_limits::max(); + template + inline const std::vector>>& + MeshT::getFaces() const noexcept + { + return (m_faces); + } - switch (a_algorithm) { - case SearchAlgorithm::Direct: { - minDist = this->DirectSignedDistance(a_point); + template + inline std::vector> + MeshT::getAllVertexCoordinates() const noexcept + { + std::vector vertexCoordinates; + for (const auto& v : m_vertices) { + vertexCoordinates.emplace_back(v->getPosition()); + } - break; + return vertexCoordinates; } - case SearchAlgorithm::Direct2: { - minDist = this->DirectSignedDistance2(a_point); - break; + template + inline T + MeshT::signedDistance(const Vec3& a_point) const noexcept + { + return this->signedDistance(a_point, m_algorithm); } - default: { - std::cerr << "Error in file 'CD_DCELMeshImplem.H' MeshT::signedDistance " - "unsupported algorithm requested\n"; - break; - } + template + inline T + MeshT::unsignedDistance2(const Vec3& a_point) const noexcept + { + T minDist2 = std::numeric_limits::max(); + + for (const auto& f : m_faces) { + const T curDist2 = f->unsignedDistance2(a_point); + + minDist2 = std::min(minDist2, curDist2); + } + + return minDist2; } - return minDist; -} + template + inline T + MeshT::signedDistance(const Vec3& a_point, SearchAlgorithm a_algorithm) const noexcept + { + T minDist = std::numeric_limits::max(); -template -inline T -MeshT::DirectSignedDistance(const Vec3& a_point) const noexcept -{ - T minDist = m_faces.front()->signedDistance(a_point); - T minDist2 = minDist * minDist; + switch (a_algorithm) { + case SearchAlgorithm::Direct: { + minDist = this->DirectSignedDistance(a_point); - for (const auto& f : m_faces) { - const T curDist = f->signedDistance(a_point); - const T curDist2 = curDist * curDist; + break; + } + case SearchAlgorithm::Direct2: { + minDist = this->DirectSignedDistance2(a_point); - if (curDist2 < minDist2) { - minDist = curDist; - minDist2 = curDist2; + break; } + default: { + std::cerr << "Error in file 'CD_DCELMeshImplem.H' MeshT::signedDistance " + "unsupported algorithm requested\n"; + + break; + } + } + + return minDist; } - return minDist; -} + template + inline T + MeshT::DirectSignedDistance(const Vec3& a_point) const noexcept + { + T minDist = m_faces.front()->signedDistance(a_point); + T minDist2 = minDist * minDist; + + for (const auto& f : m_faces) { + const T curDist = f->signedDistance(a_point); + const T curDist2 = curDist * curDist; + + if (curDist2 < minDist2) { + minDist = curDist; + minDist2 = curDist2; + } + } + + return minDist; + } -template -inline T -MeshT::DirectSignedDistance2(const Vec3& a_point) const noexcept -{ - FacePtr closest = m_faces.front(); - T minDist2 = closest->unsignedDistance2(a_point); + template + inline T + MeshT::DirectSignedDistance2(const Vec3& a_point) const noexcept + { + FacePtr closest = m_faces.front(); + T minDist2 = closest->unsignedDistance2(a_point); - for (const auto& f : m_faces) { - const T curDist2 = f->unsignedDistance2(a_point); + for (const auto& f : m_faces) { + const T curDist2 = f->unsignedDistance2(a_point); - if (curDist2 < minDist2) { - closest = f; - minDist2 = curDist2; + if (curDist2 < minDist2) { + closest = f; + minDist2 = curDist2; + } } - } - return closest->signedDistance(a_point); -} + return closest->signedDistance(a_point); + } } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_Polygon2D.hpp b/Source/EBGeometry_DCEL_Polygon2D.hpp deleted file mode 100644 index 27d8e882..00000000 --- a/Source/EBGeometry_DCEL_Polygon2D.hpp +++ /dev/null @@ -1,192 +0,0 @@ -/* EBGeometry - * Copyright © 2022 Robert Marskar - * Please refer to Copyright.txt and LICENSE in the EBGeometry root directory. - */ -/*! - @file EBGeometry_Polygon2D.hpp - @brief Declaration of a two-dimensional polygon class for embedding 3D - polygon faces - @author Robert Marskar -*/ - -#ifndef EBGeometry_Polygon2D -#define EBGeometry_Polygon2D - -// Std includes -#include -#include - -// Our includes -#include "EBGeometry_Vec.hpp" -#include "EBGeometry_NamespaceHeader.hpp" - -namespace DCEL { - -/*! - @brief Class for embedding a DCEL polygon face into 2D. - @details This class is required for determining whether or not a 3D point - projected to the plane of an N-sided polygon lies inside or outside the - polygon face. To do this we compute the 2D embedding of the polygon face, - reducing the problem to a tractable dimension where we can use well-tested - algorithm. The 2D embedding of a polygon occurs by taking a set of 3D points - and a corresponding normal vector, and projecting those points along one of - the 3D Cartesian axes such that the polygon has the largest area. In essence, - we simply find the direction with the smallest normal vector component and - ignore that. Once the 2D embedding is computed, we can use well-known - algorithms for checking if a point lies inside or outside. The supported - algorithms are 1) The winding number algorithm (computing the winding number), - 2) Computing the subtended angle of the point with the edges of the polygon - (sums to 360 degrees if the point is inside), or computing the crossing number - which checks how many times a ray cast from the point crosses the edges of the - polygon. -*/ -template -class Polygon2D -{ -public: - /*! - @brief Supported algorithms for performing inside/outside tests when - checking if a point projects to the inside or outside of a polygon face. - */ - enum class InsideOutsideAlgorithm - { - SubtendedAngle, - CrossingNumber, - WindingNumber - }; - - /*! - @brief Alias to cut down on typing - */ - using Vec2 = Vec2T; - - /*! - @brief Alias to cut down on typing - */ - using Vec3 = Vec3T; - - /*! - @brief Disallowed constructor, use the one with the normal vector and points - */ - Polygon2D() = delete; - - /*! - @brief Full constructor - @param[in] a_normal Normal vector of the 3D polygon face - @param[in] a_points Vertex coordinates of the 3D polygon face - */ - Polygon2D(const Vec3& a_normal, const std::vector& a_points); - - /*! - @brief Destructor (does nothing - */ - ~Polygon2D() = default; - - /*! - @brief Check if a point is inside or outside the 2D polygon - @param[in] a_point 3D point coordinates - @param[in] a_algorithm Inside/outside algorithm - @details This will call the function corresponding to a_algorithm. - */ - inline bool - isPointInside(const Vec3& a_point, const InsideOutsideAlgorithm a_algorithm) const noexcept; - - /*! - @brief Check if a point is inside a 2D polygon, using the winding number - algorithm - @param[in] a_point 3D point coordinates - @return Returns true if the 3D point projects to the inside of the 2D - polygon - */ - inline bool - isPointInsidePolygonWindingNumber(const Vec3& a_point) const noexcept; - - /*! - @brief Check if a point is inside a 2D polygon, using the subtended angles - @param[in] a_point 3D point coordinates - @return Returns true if the 3D point projects to the inside of the 2D - polygon - */ - inline bool - isPointInsidePolygonSubtend(const Vec3& a_point) const noexcept; - - /*! - @brief Check if a point is inside a 2D polygon, by computing the number of - times a ray crosses the polygon edges. - @param[in] a_point 3D point coordinates - @return Returns true if the 3D point projects to the inside of the 2D - polygon - */ - inline bool - isPointInsidePolygonCrossingNumber(const Vec3& a_point) const noexcept; - -private: - /*! - @brief 3D coordinate direction to ignore - */ - size_t m_ignoreDir; - - /*! - @brief The corresponding 2D x-direction. - */ - size_t m_xDir; - - /*! - @brief The corresponding 2D y-direction. - */ - size_t m_yDir; - - /*! - @brief Projected set of points in 2D - */ - std::vector m_points; - - /*! - @brief Project a 3D point onto the 2D polygon plane (this ignores one of the - vector components) - @param[in] a_poitn 3D point - @return 2D point, ignoring one of the coordinate directions. - */ - inline Vec2 - projectPoint(const Vec3& a_point) const noexcept; - - /*! - @brief Define function. This find the direction to ignore and then computes - the 2D points. - @param[in] a_normal Normal vector for polygon face - @param[in] a_points Vertex coordinates for polygon face. - */ - inline void - define(const Vec3& a_normal, const std::vector& a_points); - - /*! - @brief Compute the winding number for a point P with the 2D polygon - @param[in] P 2D point - @return Returns winding number. - */ - inline int - computeWindingNumber(const Vec2& P) const noexcept; - - /*! - @brief Compute the crossing number for a point P with the 2D polygon - @param[in] P 2D point - @return Returns crossing number. - */ - inline size_t - computeCrossingNumber(const Vec2& P) const noexcept; - - /*! - @brief Compute the subtended angle for a point P with the 2D polygon - @param[in] P 2D point - @return Returns subtended angle. - */ - inline T - computeSubtendedAngle(const Vec2& P) const noexcept; -}; -} // namespace DCEL - -#include "EBGeometry_NamespaceFooter.hpp" - -#include "EBGeometry_Polygon2DImplem.hpp" - -#endif diff --git a/Source/EBGeometry_DCEL_Polygon2DImplem.hpp b/Source/EBGeometry_DCEL_Polygon2DImplem.hpp deleted file mode 100644 index 702c815d..00000000 --- a/Source/EBGeometry_DCEL_Polygon2DImplem.hpp +++ /dev/null @@ -1,225 +0,0 @@ -/* EBGeometry - * Copyright © 2022 Robert Marskar - * Please refer to Copyright.txt and LICENSE in the EBGeometry root directory. - */ - -/*! - @file EBGeometry_Polygon2DImplem.hpp - @brief Implementation of DCELPolygon.hpp - @author Robert Marskar -*/ - -#ifndef EBGeometry_Polygon2DImplem -#define EBGeometry_Polygon2DImplem - -// Std includes -#include - -// Our includes -#include "EBGeometry_Polygon2D.hpp" -#include "EBGeometry_NamespaceHeader.hpp" - -namespace DCEL { - -template -inline Polygon2D::Polygon2D(const Vec3& a_normal, const std::vector& a_points) -{ - this->define(a_normal, a_points); -} - -template -inline bool -Polygon2D::isPointInside(const Vec3& a_point, const InsideOutsideAlgorithm a_algorithm) const noexcept -{ - bool ret = false; - - switch (a_algorithm) { - case InsideOutsideAlgorithm::SubtendedAngle: { - ret = this->isPointInsidePolygonSubtend(a_point); - - break; - } - case InsideOutsideAlgorithm::CrossingNumber: { - ret = this->isPointInsidePolygonCrossingNumber(a_point); - - break; - } - case InsideOutsideAlgorithm::WindingNumber: { - ret = this->isPointInsidePolygonWindingNumber(a_point); - - break; - } - default: - std::cerr << "In file 'CD_DCELPolygon2DImplem.H' function " - "Polygon2D::isPointInside - logic bust.\n"; - } - - return ret; -} - -template -inline Vec2T -Polygon2D::projectPoint(const Vec3& a_point) const noexcept -{ - return Vec2(a_point[m_xDir], a_point[m_yDir]); -} - -template -inline void -Polygon2D::define(const Vec3& a_normal, const std::vector& a_points) -{ - m_ignoreDir = 0; - - for (size_t dir = 1; dir < 3; dir++) { - if (std::abs(a_normal[dir]) > std::abs(a_normal[m_ignoreDir])) { - m_ignoreDir = dir; - } - } - - m_xDir = 3; - m_yDir = 0; - - for (size_t dir = 0; dir < 3; dir++) { - if (dir != m_ignoreDir) { - m_xDir = std::min(m_xDir, dir); - m_yDir = std::max(m_yDir, dir); - } - } - - for (const auto& p3 : a_points) { - m_points.emplace_back(this->projectPoint(p3)); - } -} - -template -inline int -Polygon2D::computeWindingNumber(const Vec2& P) const noexcept -{ - int wn = 0; // the winding number counter - - const size_t N = m_points.size(); - - auto isLeft = [](const Vec2& P0, const Vec2& P1, const Vec2& P2) { - return (P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y); - }; - - // loop through all edges of the polygon - for (size_t i = 0; i < N; i++) { // edge from V[i] to V[i+1] - - const Vec2& P1 = m_points[i]; - const Vec2& P2 = m_points[(i + 1) % N]; - - const T res = isLeft(P1, P2, P); - - if (P1.y <= P.y) { // start y <= P.y - if (P2.y > P.y) // an upward crossing - if (res > 0.) // P left of edge - ++wn; // have a valid up intersect - } else { // start y > P.y (no test needed) - if (P2.y <= P.y) // a downward crossing - if (res < 0.) // P right of edge - --wn; // have a valid down intersect - } - } - - return wn; -} - -template -inline size_t -Polygon2D::computeCrossingNumber(const Vec2& P) const noexcept -{ - size_t cn = 0; - - const size_t N = m_points.size(); - - for (size_t i = 0; i < N; i++) { // edge from V[i] to V[i+1] - const Vec2& P1 = m_points[i]; - const Vec2& P2 = m_points[(i + 1) % N]; - - const bool upwardCrossing = (P1.y <= P.y) && (P2.y > P.y); - const bool downwardCrossing = (P1.y > P.y) && (P2.y <= P.y); - - if (upwardCrossing || downwardCrossing) { - const T t = (P.y - P1.y) / (P2.y - P1.y); - - if (P.x < P1.x + t * (P2.x - P1.x)) { // P.x < intersect - cn += 1; // a valid crossing of y=P.y right of P.x - } - } - } - - return cn; -} - -template -inline T -Polygon2D::computeSubtendedAngle(const Vec2& p) const noexcept -{ - T sumTheta = 0.0; - - const size_t N = m_points.size(); - - for (size_t i = 0; i < N; i++) { - const Vec2 p1 = m_points[i] - p; - const Vec2 p2 = m_points[(i + 1) % N] - p; - - const T theta1 = atan2(p1.y, p1.x); - const T theta2 = atan2(p2.y, p2.x); - - T dTheta = theta2 - theta1; - - while (dTheta > M_PI) - dTheta -= 2.0 * M_PI; - while (dTheta < -M_PI) - dTheta += 2.0 * M_PI; - - sumTheta += dTheta; - } - - return sumTheta; -} - -template -inline bool -Polygon2D::isPointInsidePolygonWindingNumber(const Vec3& a_point) const noexcept -{ - const Vec2 p = this->projectPoint(a_point); - - const int wn = this->computeWindingNumber(p); - - return wn != 0; -} - -template -inline bool -Polygon2D::isPointInsidePolygonCrossingNumber(const Vec3& a_point) const noexcept -{ - const Vec2 p = this->projectPoint(a_point); - - const size_t cn = this->computeCrossingNumber(p); - - const bool ret = (cn & 1); - - return ret; -} - -template -inline bool -Polygon2D::isPointInsidePolygonSubtend(const Vec3& a_point) const noexcept -{ - const Vec2 p = this->projectPoint(a_point); - - T sumTheta = this->computeSubtendedAngle(p); // Should be = 2pi if point is inside. - - sumTheta = std::abs(sumTheta) / (2. * M_PI); - - const bool ret = (round(sumTheta) == 1); // 2PI if the polygon is inside. - - return ret; -} -} // namespace DCEL - -#include "EBGeometry_NamespaceFooter.hpp" - -#endif diff --git a/Source/EBGeometry_DCEL_Vertex.hpp b/Source/EBGeometry_DCEL_Vertex.hpp index 0b95ec63..1c829f47 100644 --- a/Source/EBGeometry_DCEL_Vertex.hpp +++ b/Source/EBGeometry_DCEL_Vertex.hpp @@ -25,281 +25,281 @@ namespace DCEL { -// Forward declare classes. -template -class VertexT; -template -class EdgeT; -template -class FaceT; -template -class EdgeIteratorT; - -/*! - @brief Class which represents a vertex node in a double-edge connected list - (DCEL). - @details This class is used in DCEL functionality which stores polygonal - surfaces in a mesh. The VertexT class has a position, a normal vector, and a - pointer to one of the outgoing edges from the vertex. For performance reasons - we also include pointers to all the polygon faces that share this vertex. - @note The normal vector is outgoing, i.e. a point x is "outside" the vertex if - the dot product between n and (x - x0) is positive. -*/ -template -class VertexT -{ -public: - /*! - @brief Alias to cut down on typing - */ - using Vec3 = Vec3T; - - /*! - @brief Alias to cut down on typing - */ - using Vertex = VertexT; - - /*! - @brief Alias to cut down on typing - */ - using Edge = EdgeT; - - /*! - @brief Alias to cut down on typing - */ - using Face = FaceT; - - /*! - @brief Alias to cut down on typing. Note that this is - std::shared_ptr > - */ - using VertexPtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing. Note that this is - std::shared_ptr > - */ - using EdgePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing. Note that this is - std::shared_ptr > - */ - using FacePtr = std::shared_ptr; - - /*! - @brief Alias to cut down on typing. Note that this is - std::shared_ptr > - */ - using EdgeIterator = EdgeIteratorT; - - /*! - @brief Default constructor. - @details This initializes the position and the normal vector to zero - vectors, and the polygon face list is empty - */ - VertexT(); - - /*! - @brief Partial constructor. - @param[in] a_position Vertex position - @details This initializes the position to a_position and the normal vector - to the zero vector. The polygon face list is empty. - */ - VertexT(const Vec3& a_position); - - /*! - @brief Constructor. - @param[in] a_position Vertex position - @param[in] a_normal Vertex normal vector - @details This initializes the position to a_position and the normal vector - to a_normal. The polygon face list is empty. - */ - VertexT(const Vec3& a_position, const Vec3& a_normal); - - /*! - @brief Full copy constructor - @param[in] a_otherVertex Other vertex - @details This copies the position, normal vector, and outgoing edge pointer - from the other vertex. The polygon face list. - */ - VertexT(const Vertex& a_otherVertex); - - /*! - @brief Destructor (does nothing) - */ - ~VertexT(); - - /*! - @brief Define function - @param[in] a_position Vertex position - @param[in] a_edge Pointer to outgoing edge - @param[in] a_normal Vertex normal vector - @details This sets the position, normal vector, and edge pointer. - */ - inline void - define(const Vec3& a_position, const EdgePtr& a_edge, const Vec3& a_normal) noexcept; - - /*! - @brief Set the vertex position - @param[in] a_position Vertex position - */ - inline void - setPosition(const Vec3& a_position) noexcept; - - /*! - @brief Set the vertex normal vector - @param[in] a_normal Vertex normal vector - */ - inline void - setNormal(const Vec3& a_normal) noexcept; - - /*! - @brief Set the reference to the outgoing edge - @param[in] a_edge Pointer to an outgoing edge - */ - inline void - setEdge(const EdgePtr& a_edge) noexcept; - - /*! - @brief Add a face to the polygon face list. - @param[in] a_face Pointer to face. - */ - inline void - addFace(const FacePtr& a_face) noexcept; - - /*! - @brief Normalize the normal vector, ensuring its length is 1 - */ - inline void - normalizeNormalVector() noexcept; - - /*! - @brief Compute the vertex normal, using an average the normal vector in this - vertex's face list (m_faces) - */ - inline void - computeVertexNormalAverage() noexcept; - - /*! - @brief Compute the vertex normal, using an average of the normal vectors in - the input face list - @param[in] a_faces Faces - @note This computes the vertex normal as n = sum(normal(face))/num(faces) - */ - inline void - computeVertexNormalAverage(const std::vector& a_faces) noexcept; - - /*! - @brief Compute the vertex normal, using the pseudonormal algorithm which - weights the normal with the subtended angle to each connected face. - @details This calls the other version with a_faces = m_faces - @note This computes the normal vector using the pseudnormal algorithm from - Baerentzen and Aanes in "Signed distance computation using the angle - weighted pseudonormal" (DOI: 10.1109/TVCG.2005.49) - */ - inline void - computeVertexNormalAngleWeighted() noexcept; - - /*! - @brief Compute the vertex normal, using the pseudonormal algorithm which - weights the normal with the subtended angle to each connected face. - @param[in] a_faces Faces to use for computation. - @note This computes the normal vector using the pseudnormal algorithm from - Baerentzen and Aanes in "Signed distance computation using the angle - weighted pseudonormal" (DOI: 10.1109/TVCG.2005.49) - */ - inline void - computeVertexNormalAngleWeighted(const std::vector& a_faces) noexcept; - - /*! - @brief Return modifiable vertex position. - */ - inline Vec3T& - getPosition() noexcept; - - /*! - @brief Return immutable vertex position. - */ - inline const Vec3T& - getPosition() const noexcept; - - /*! - @brief Return modifiable vertex normal vector. - */ - inline Vec3T& - getNormal() noexcept; - - /*! - @brief Return immutable vertex normal vector. - */ - inline const Vec3T& - getNormal() const noexcept; - - /*! - @brief Return modifiable pointer to outgoing edge. - */ - inline EdgePtr& - getOutgoingEdge() noexcept; - - /*! - @brief Return immutable pointer to outgoing edge. - */ - inline const EdgePtr& - getOutgoingEdge() const noexcept; - - /*! - @brief Get modifiable polygon face list for this vertex. - */ - inline std::vector& - getFaces() noexcept; - - /*! - @brief Get immutable polygon face list for this vertex. - */ - inline const std::vector& - getFaces() const noexcept; - - /*! - @brief Get the signed distance to this vertex - @param[in] a_x0 Position in space. - @return The returned distance is |a_x0 - m_position| and the sign is given - by the sign of m_normal * |a_x0 - m_position|. - */ - inline T - signedDistance(const Vec3& a_x0) const noexcept; - - /*! - @brief Get the squared unsigned distance to this vertex - @details This is faster to compute than signedDistance, and might be - preferred for some algorithms. - @return Returns the vector length of (a_x - m_position) - */ - inline T - unsignedDistance2(const Vec3& a_x0) const noexcept; - -protected: - /*! - @brief Pointer to an outgoing edge from this vertex. - */ - EdgePtr m_outgoingEdge; - - /*! - @brief Vertex position - */ - Vec3 m_position; - - /*! - @brief Vertex normal vector - */ - Vec3 m_normal; - - /*! - @brief List of faces connected to this vertex (these must be "manually" - added) - */ - std::vector m_faces; -}; + // Forward declare classes. + template + class VertexT; + template + class EdgeT; + template + class FaceT; + template + class EdgeIteratorT; + + /*! + @brief Class which represents a vertex node in a double-edge connected list + (DCEL). + @details This class is used in DCEL functionality which stores polygonal + surfaces in a mesh. The VertexT class has a position, a normal vector, and a + pointer to one of the outgoing edges from the vertex. For performance reasons + we also include pointers to all the polygon faces that share this vertex. + @note The normal vector is outgoing, i.e. a point x is "outside" the vertex if + the dot product between n and (x - x0) is positive. + */ + template + class VertexT + { + public: + /*! + @brief Alias to cut down on typing + */ + using Vec3 = Vec3T; + + /*! + @brief Alias to cut down on typing + */ + using Vertex = VertexT; + + /*! + @brief Alias to cut down on typing + */ + using Edge = EdgeT; + + /*! + @brief Alias to cut down on typing + */ + using Face = FaceT; + + /*! + @brief Alias to cut down on typing. Note that this is + std::shared_ptr > + */ + using VertexPtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing. Note that this is + std::shared_ptr > + */ + using EdgePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing. Note that this is + std::shared_ptr > + */ + using FacePtr = std::shared_ptr; + + /*! + @brief Alias to cut down on typing. Note that this is + std::shared_ptr > + */ + using EdgeIterator = EdgeIteratorT; + + /*! + @brief Default constructor. + @details This initializes the position and the normal vector to zero + vectors, and the polygon face list is empty + */ + VertexT(); + + /*! + @brief Partial constructor. + @param[in] a_position Vertex position + @details This initializes the position to a_position and the normal vector + to the zero vector. The polygon face list is empty. + */ + VertexT(const Vec3& a_position); + + /*! + @brief Constructor. + @param[in] a_position Vertex position + @param[in] a_normal Vertex normal vector + @details This initializes the position to a_position and the normal vector + to a_normal. The polygon face list is empty. + */ + VertexT(const Vec3& a_position, const Vec3& a_normal); + + /*! + @brief Full copy constructor + @param[in] a_otherVertex Other vertex + @details This copies the position, normal vector, and outgoing edge pointer + from the other vertex. The polygon face list. + */ + VertexT(const Vertex& a_otherVertex); + + /*! + @brief Destructor (does nothing) + */ + ~VertexT(); + + /*! + @brief Define function + @param[in] a_position Vertex position + @param[in] a_edge Pointer to outgoing edge + @param[in] a_normal Vertex normal vector + @details This sets the position, normal vector, and edge pointer. + */ + inline void + define(const Vec3& a_position, const EdgePtr& a_edge, const Vec3& a_normal) noexcept; + + /*! + @brief Set the vertex position + @param[in] a_position Vertex position + */ + inline void + setPosition(const Vec3& a_position) noexcept; + + /*! + @brief Set the vertex normal vector + @param[in] a_normal Vertex normal vector + */ + inline void + setNormal(const Vec3& a_normal) noexcept; + + /*! + @brief Set the reference to the outgoing edge + @param[in] a_edge Pointer to an outgoing edge + */ + inline void + setEdge(const EdgePtr& a_edge) noexcept; + + /*! + @brief Add a face to the polygon face list. + @param[in] a_face Pointer to face. + */ + inline void + addFace(const FacePtr& a_face) noexcept; + + /*! + @brief Normalize the normal vector, ensuring its length is 1 + */ + inline void + normalizeNormalVector() noexcept; + + /*! + @brief Compute the vertex normal, using an average the normal vector in this + vertex's face list (m_faces) + */ + inline void + computeVertexNormalAverage() noexcept; + + /*! + @brief Compute the vertex normal, using an average of the normal vectors in + the input face list + @param[in] a_faces Faces + @note This computes the vertex normal as n = sum(normal(face))/num(faces) + */ + inline void + computeVertexNormalAverage(const std::vector& a_faces) noexcept; + + /*! + @brief Compute the vertex normal, using the pseudonormal algorithm which + weights the normal with the subtended angle to each connected face. + @details This calls the other version with a_faces = m_faces + @note This computes the normal vector using the pseudnormal algorithm from + Baerentzen and Aanes in "Signed distance computation using the angle + weighted pseudonormal" (DOI: 10.1109/TVCG.2005.49) + */ + inline void + computeVertexNormalAngleWeighted() noexcept; + + /*! + @brief Compute the vertex normal, using the pseudonormal algorithm which + weights the normal with the subtended angle to each connected face. + @param[in] a_faces Faces to use for computation. + @note This computes the normal vector using the pseudnormal algorithm from + Baerentzen and Aanes in "Signed distance computation using the angle + weighted pseudonormal" (DOI: 10.1109/TVCG.2005.49) + */ + inline void + computeVertexNormalAngleWeighted(const std::vector& a_faces) noexcept; + + /*! + @brief Return modifiable vertex position. + */ + inline Vec3T& + getPosition() noexcept; + + /*! + @brief Return immutable vertex position. + */ + inline const Vec3T& + getPosition() const noexcept; + + /*! + @brief Return modifiable vertex normal vector. + */ + inline Vec3T& + getNormal() noexcept; + + /*! + @brief Return immutable vertex normal vector. + */ + inline const Vec3T& + getNormal() const noexcept; + + /*! + @brief Return modifiable pointer to outgoing edge. + */ + inline EdgePtr& + getOutgoingEdge() noexcept; + + /*! + @brief Return immutable pointer to outgoing edge. + */ + inline const EdgePtr& + getOutgoingEdge() const noexcept; + + /*! + @brief Get modifiable polygon face list for this vertex. + */ + inline std::vector& + getFaces() noexcept; + + /*! + @brief Get immutable polygon face list for this vertex. + */ + inline const std::vector& + getFaces() const noexcept; + + /*! + @brief Get the signed distance to this vertex + @param[in] a_x0 Position in space. + @return The returned distance is |a_x0 - m_position| and the sign is given + by the sign of m_normal * |a_x0 - m_position|. + */ + inline T + signedDistance(const Vec3& a_x0) const noexcept; + + /*! + @brief Get the squared unsigned distance to this vertex + @details This is faster to compute than signedDistance, and might be + preferred for some algorithms. + @return Returns the vector length of (a_x - m_position) + */ + inline T + unsignedDistance2(const Vec3& a_x0) const noexcept; + + protected: + /*! + @brief Pointer to an outgoing edge from this vertex. + */ + EdgePtr m_outgoingEdge; + + /*! + @brief Vertex position + */ + Vec3 m_position; + + /*! + @brief Vertex normal vector + */ + Vec3 m_normal; + + /*! + @brief List of faces connected to this vertex (these must be "manually" + added) + */ + std::vector m_faces; + }; } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_DCEL_VertexImplem.hpp b/Source/EBGeometry_DCEL_VertexImplem.hpp index 7dddf7e2..2477822b 100644 --- a/Source/EBGeometry_DCEL_VertexImplem.hpp +++ b/Source/EBGeometry_DCEL_VertexImplem.hpp @@ -21,273 +21,274 @@ namespace DCEL { -template -inline VertexT::VertexT() -{ - m_position = Vec3::zero(); - m_normal = Vec3::zero(); - - m_faces.resize(0); -} - -template -inline VertexT::VertexT(const Vec3& a_position) : VertexT() -{ - m_position = a_position; -} - -template -inline VertexT::VertexT(const Vec3& a_position, const Vec3& a_normal) : VertexT() -{ - m_position = a_position; - m_normal = a_normal; -} - -template -inline VertexT::VertexT(const VertexT& a_otherVertex) -{ - m_position = a_otherVertex.m_position; - m_normal = a_otherVertex.m_m_normal; - m_outgoingEdge = a_otherVertex.m_outgoingEdge; -} - -template -inline VertexT::~VertexT() -{ -} - -template -inline void -VertexT::define(const Vec3& a_position, const EdgePtr& a_edge, const Vec3& a_normal) noexcept -{ - m_position = a_position; - m_outgoingEdge = a_edge; - m_normal = a_normal; -} - -template -inline void -VertexT::setPosition(const Vec3& a_position) noexcept -{ - m_position = a_position; -} - -template -inline void -VertexT::setEdge(const EdgePtr& a_edge) noexcept -{ - m_outgoingEdge = a_edge; -} - -template -inline void -VertexT::setNormal(const Vec3& a_normal) noexcept -{ - m_normal = a_normal; -} - -template -inline void -VertexT::addFace(const FacePtr& a_face) noexcept -{ - m_faces.emplace_back(a_face); -} - -template -inline void -VertexT::normalizeNormalVector() noexcept -{ - m_normal = m_normal / m_normal.length(); -} - -template -inline void -VertexT::computeVertexNormalAverage() noexcept -{ - this->computeVertexNormalAverage(m_faces); -} - -template -inline void -VertexT::computeVertexNormalAverage(const std::vector& a_faces) noexcept -{ - m_normal = Vec3::zero(); - - // TLDR: We simply compute the sum of the normal vectors for each face in - // a_faces and then normalize. This - // will yield an "average" of the normal vectors of the faces - // circulating this vertex. - for (const auto& f : a_faces) { - m_normal += f->getNormal(); + template + inline VertexT::VertexT() + { + m_position = Vec3::zero(); + m_normal = Vec3::zero(); + + m_faces.resize(0); + } + + template + inline VertexT::VertexT(const Vec3& a_position) : VertexT() + { + m_position = a_position; + } + + template + inline VertexT::VertexT(const Vec3& a_position, const Vec3& a_normal) : VertexT() + { + m_position = a_position; + m_normal = a_normal; + } + + template + inline VertexT::VertexT(const VertexT& a_otherVertex) + { + m_position = a_otherVertex.m_position; + m_normal = a_otherVertex.m_m_normal; + m_outgoingEdge = a_otherVertex.m_outgoingEdge; + } + + template + inline VertexT::~VertexT() + {} + + template + inline void + VertexT::define(const Vec3& a_position, const EdgePtr& a_edge, const Vec3& a_normal) noexcept + { + m_position = a_position; + m_outgoingEdge = a_edge; + m_normal = a_normal; + } + + template + inline void + VertexT::setPosition(const Vec3& a_position) noexcept + { + m_position = a_position; + } + + template + inline void + VertexT::setEdge(const EdgePtr& a_edge) noexcept + { + m_outgoingEdge = a_edge; + } + + template + inline void + VertexT::setNormal(const Vec3& a_normal) noexcept + { + m_normal = a_normal; + } + + template + inline void + VertexT::addFace(const FacePtr& a_face) noexcept + { + m_faces.emplace_back(a_face); + } + + template + inline void + VertexT::normalizeNormalVector() noexcept + { + m_normal = m_normal / m_normal.length(); + } + + template + inline void + VertexT::computeVertexNormalAverage() noexcept + { + this->computeVertexNormalAverage(m_faces); + } + + template + inline void + VertexT::computeVertexNormalAverage(const std::vector& a_faces) noexcept + { + m_normal = Vec3::zero(); + + // TLDR: We simply compute the sum of the normal vectors for each face in + // a_faces and then normalize. This + // will yield an "average" of the normal vectors of the faces + // circulating this vertex. + for (const auto& f : a_faces) { + m_normal += f->getNormal(); + } + + this->normalizeNormalVector(); + } + + template + inline void + VertexT::computeVertexNormalAngleWeighted() noexcept + { + this->computeVertexNormalAngleWeighted(m_faces); } - this->normalizeNormalVector(); -} - -template -inline void -VertexT::computeVertexNormalAngleWeighted() noexcept -{ - this->computeVertexNormalAngleWeighted(m_faces); -} - -template -inline void -VertexT::computeVertexNormalAngleWeighted(const std::vector& a_faces) noexcept -{ - m_normal = Vec3::zero(); - - // This routine computes the pseudonormal from pseudnormal algorithm from - // Baerentzen and Aanes in "Signed distance computation using the angle - // weighted pseudonormal" (DOI: 10.1109/TVCG.2005.49). This algorithm computes - // an average normal vector using the normal vectors of each face connected to - // this vertex, i.e. in the form - // - // n = sum(w * n(face))/sum(w) - // - // where w are weights for each face. This weight is given by the subtended - // angle of the face, which means the angle spanned by the incoming/outgoing - // edges of the face that pass through this vertex. - // - // - // The below code is more complicated than it looks. It happens because we - // want the two half edges that has the current vertex as a mutual vertex - // (i.e. the "incoming" and "outgoing" edges into this vertex). Normally we'd - // just iterate through edges, but if it happens that an input face is - // flipped, this will result in infinite iteration. Instead, we have stored - // the pointers to each face connected to this vertex. We look through each - // face to find the endpoints of the edges the have the current vertex as the - // common vertex, and then compute the subtended angle between those. Sigh... - - const VertexPtr& originVertex = m_outgoingEdge->getVertex(); // AKA 'this' - - for (const auto& f : a_faces) { - - std::vector inoutVertices(0); - for (EdgeIterator edgeIt(f->getHalfEdge()); edgeIt.ok(); ++edgeIt) { - const auto& e = edgeIt(); - - const auto& v1 = e->getVertex(); - const auto& v2 = e->getOtherVertex(); - - if (v1 == originVertex || v2 == originVertex) { - if (v1 == originVertex) { - inoutVertices.emplace_back(v2); - } else if (v2 == originVertex) { - inoutVertices.emplace_back(v1); - } else { - std::cerr << "In file 'CD_DCELVertexImplem.H' function " - "vertexT::computeVertexNormalAngleWeighted() - logic bust.\n"; + template + inline void + VertexT::computeVertexNormalAngleWeighted(const std::vector& a_faces) noexcept + { + m_normal = Vec3::zero(); + + // This routine computes the pseudonormal from pseudnormal algorithm from + // Baerentzen and Aanes in "Signed distance computation using the angle + // weighted pseudonormal" (DOI: 10.1109/TVCG.2005.49). This algorithm computes + // an average normal vector using the normal vectors of each face connected to + // this vertex, i.e. in the form + // + // n = sum(w * n(face))/sum(w) + // + // where w are weights for each face. This weight is given by the subtended + // angle of the face, which means the angle spanned by the incoming/outgoing + // edges of the face that pass through this vertex. + // + // + // The below code is more complicated than it looks. It happens because we + // want the two half edges that has the current vertex as a mutual vertex + // (i.e. the "incoming" and "outgoing" edges into this vertex). Normally we'd + // just iterate through edges, but if it happens that an input face is + // flipped, this will result in infinite iteration. Instead, we have stored + // the pointers to each face connected to this vertex. We look through each + // face to find the endpoints of the edges the have the current vertex as the + // common vertex, and then compute the subtended angle between those. Sigh... + + const VertexPtr& originVertex = m_outgoingEdge->getVertex(); // AKA 'this' + + for (const auto& f : a_faces) { + + std::vector inoutVertices(0); + for (EdgeIterator edgeIt(f->getHalfEdge()); edgeIt.ok(); ++edgeIt) { + const auto& e = edgeIt(); + + const auto& v1 = e->getVertex(); + const auto& v2 = e->getOtherVertex(); + + if (v1 == originVertex || v2 == originVertex) { + if (v1 == originVertex) { + inoutVertices.emplace_back(v2); + } + else if (v2 == originVertex) { + inoutVertices.emplace_back(v1); + } + else { + std::cerr << "In file 'CD_DCELVertexImplem.H' function " + "vertexT::computeVertexNormalAngleWeighted() - logic bust.\n"; + } } } - } - if (inoutVertices.size() != 2) { - std::cerr << "In file 'CD_DCELVertexImplem.H' function " - "vertexT::computeVertexNormalAngleWeighted() - logic bust 2.\n"; - } + if (inoutVertices.size() != 2) { + std::cerr << "In file 'CD_DCELVertexImplem.H' function " + "vertexT::computeVertexNormalAngleWeighted() - logic bust 2.\n"; + } + + const Vec3& x0 = originVertex->getPosition(); + const Vec3& x1 = inoutVertices[0]->getPosition(); + const Vec3& x2 = inoutVertices[1]->getPosition(); + + if (x0 == x1 || x0 == x2 || x1 == x2) { + std::cerr << "In file 'CD_DCELVertexImplem.H' function " + "vertexT::computeVertexNormalAngleWeighted() - logic bust 3.\n"; + } + + Vec3 v1 = x1 - x0; + Vec3 v2 = x2 - x0; - const Vec3& x0 = originVertex->getPosition(); - const Vec3& x1 = inoutVertices[0]->getPosition(); - const Vec3& x2 = inoutVertices[1]->getPosition(); + v1 = v1 / v1.length(); + v2 = v2 / v2.length(); - if (x0 == x1 || x0 == x2 || x1 == x2) { - std::cerr << "In file 'CD_DCELVertexImplem.H' function " - "vertexT::computeVertexNormalAngleWeighted() - logic bust 3.\n"; + const Vec3& norm = f->getNormal(); + + const T alpha = acos(v1.dot(v2)); + + m_normal += alpha * norm; } - Vec3 v1 = x1 - x0; - Vec3 v2 = x2 - x0; + this->normalizeNormalVector(); + } + + template + inline Vec3T& + VertexT::getPosition() noexcept + { + return (m_position); + } + + template + inline const Vec3T& + VertexT::getPosition() const noexcept + { + return (m_position); + } - v1 = v1 / v1.length(); - v2 = v2 / v2.length(); + template + inline Vec3T& + VertexT::getNormal() noexcept + { + return (m_normal); + } - const Vec3& norm = f->getNormal(); + template + inline const Vec3T& + VertexT::getNormal() const noexcept + { + return (m_normal); + } - const T alpha = acos(v1.dot(v2)); + template + inline std::shared_ptr>& + VertexT::getOutgoingEdge() noexcept + { + return (m_outgoingEdge); + } - m_normal += alpha * norm; + template + inline const std::shared_ptr>& + VertexT::getOutgoingEdge() const noexcept + { + return (m_outgoingEdge); } - this->normalizeNormalVector(); -} - -template -inline Vec3T& -VertexT::getPosition() noexcept -{ - return (m_position); -} - -template -inline const Vec3T& -VertexT::getPosition() const noexcept -{ - return (m_position); -} - -template -inline Vec3T& -VertexT::getNormal() noexcept -{ - return (m_normal); -} - -template -inline const Vec3T& -VertexT::getNormal() const noexcept -{ - return (m_normal); -} - -template -inline std::shared_ptr>& -VertexT::getOutgoingEdge() noexcept -{ - return (m_outgoingEdge); -} - -template -inline const std::shared_ptr>& -VertexT::getOutgoingEdge() const noexcept -{ - return (m_outgoingEdge); -} - -template -inline std::vector>>& -VertexT::getFaces() noexcept -{ - return (m_faces); -} - -template -inline const std::vector>>& -VertexT::getFaces() const noexcept -{ - return (m_faces); -} - -template -inline T -VertexT::signedDistance(const Vec3& a_x0) const noexcept -{ - const auto delta = a_x0 - m_position; - const T dist = delta.length(); - const T dot = m_normal.dot(delta); - const int sign = (dot > 0.) ? 1 : -1; - - return dist * sign; -} - -template -inline T -VertexT::unsignedDistance2(const Vec3& a_x0) const noexcept -{ - const auto d = a_x0 - m_position; - - return d.dot(d); -} + template + inline std::vector>>& + VertexT::getFaces() noexcept + { + return (m_faces); + } + + template + inline const std::vector>>& + VertexT::getFaces() const noexcept + { + return (m_faces); + } + + template + inline T + VertexT::signedDistance(const Vec3& a_x0) const noexcept + { + const auto delta = a_x0 - m_position; + const T dist = delta.length(); + const T dot = m_normal.dot(delta); + const int sign = (dot > 0.) ? 1 : -1; + + return dist * sign; + } + + template + inline T + VertexT::unsignedDistance2(const Vec3& a_x0) const noexcept + { + const auto d = a_x0 - m_position; + + return d.dot(d); + } } // namespace DCEL #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_Parser.hpp b/Source/EBGeometry_Parser.hpp index 0340b5c9..4ca3ce2b 100644 --- a/Source/EBGeometry_Parser.hpp +++ b/Source/EBGeometry_Parser.hpp @@ -31,108 +31,108 @@ */ namespace Parser { -/*! - @brief Class for reading Stanford PLY files. - @note T is the precision used for storing the mesh. -*/ -template -class PLY -{ -public: - /*! - @brief Alias for cutting down on typing - */ - using Vertex = DCEL::VertexT; - - /*! - @brief Alias for cutting down on typing - */ - using Edge = DCEL::EdgeT; - - /*! - @brief Alias for cutting down on typing - */ - using Face = DCEL::FaceT; - - /*! - @brief Alias for cutting down on typing - */ - using Mesh = DCEL::MeshT; - - /*! - @brief Alias for cutting down on typing - */ - using EdgeIterator = DCEL::EdgeIteratorT; - - /*! - @brief Static function which reads an ASCII .ply file and returns a DCEL - mesh. - @param[in] a_filename File name - */ - inline static std::shared_ptr - readIntoDCEL(const std::string a_filename); - - /*! - @brief Static function which reads an ASCII .ply file and puts it in a mesh. - @param[out] a_mesh DCEL mesh - @param[in] a_filename File name - */ - inline static void - readIntoDCEL(Mesh& a_mesh, const std::string a_filename); - -protected: - /*! - @brief Read an ASCII header - @details This reads the number of vertices and faces in the PLY file. Note - that it only reads the header. - @param[out] a_numVertices Number of vertices - @param[out] a_numFaces Number of faces - @param[in,out] a_inputStream File stream. On output, the filestream is at the - end of the PLY header. - */ - inline static void - readHeaderASCII(size_t& a_numVertices, size_t& a_numFaces, std::ifstream& a_inputStream); - - /*! - @brief Read ASCII vertices. - @param[out] a_vertices DCEL vertices. These are constructed in this routine. - @param[in] a_numVertices Number of vertices to read - @param[in] a_inputStream Input file stream. - @note The next getline() from a_inputStream must read the first vertex (i.e. - don't rewind the stream before entering this routine) - */ - inline static void - readVerticesIntoDCEL( - std::vector>& a_vertices, const size_t a_numVertices, std::ifstream& a_inputStream); - - /*! - @brief Read ASCII faces and create mesh connectivity. - @param[out] a_faces DCEL faces. Constructured in this routine. - @param[out] a_edges DCEL edges. Constructured in this routine. - @param[out] a_vertices DCEL edges. Constructured in - readVerticesIntoDCEL. - @param[in] a_numFaces Total number of faces in mesh. - @param[in,out] a_inputStream Input stream - @note The next getline() from inputStream must read the first face, i.e. we - assume that read_ascii_vertices was called IMMEDIATELY before this function. - That function will center the fstream on the correct line in the input file. - */ - inline static void - readFacesIntoDCEL( - std::vector>& a_faces, - std::vector>& a_edges, - const std::vector>& a_vertices, - const size_t a_numFaces, - std::ifstream& a_inputStream); - /*! - @brief Reconcile pair edges, i.e. find the pair edge for every constructed - half-edge - @param[in,out] a_edges Half edges. + @brief Class for reading Stanford PLY files. + @note T is the precision used for storing the mesh. */ - inline static void - reconcilePairEdgesDCEL(std::vector>& a_edges); -}; + template + class PLY + { + public: + /*! + @brief Alias for cutting down on typing + */ + using Vertex = DCEL::VertexT; + + /*! + @brief Alias for cutting down on typing + */ + using Edge = DCEL::EdgeT; + + /*! + @brief Alias for cutting down on typing + */ + using Face = DCEL::FaceT; + + /*! + @brief Alias for cutting down on typing + */ + using Mesh = DCEL::MeshT; + + /*! + @brief Alias for cutting down on typing + */ + using EdgeIterator = DCEL::EdgeIteratorT; + + /*! + @brief Static function which reads an ASCII .ply file and returns a DCEL + mesh. + @param[in] a_filename File name + */ + inline static std::shared_ptr + readIntoDCEL(const std::string a_filename); + + /*! + @brief Static function which reads an ASCII .ply file and puts it in a mesh. + @param[out] a_mesh DCEL mesh + @param[in] a_filename File name + */ + inline static void + readIntoDCEL(Mesh& a_mesh, const std::string a_filename); + + protected: + /*! + @brief Read an ASCII header + @details This reads the number of vertices and faces in the PLY file. Note + that it only reads the header. + @param[out] a_numVertices Number of vertices + @param[out] a_numFaces Number of faces + @param[in,out] a_inputStream File stream. On output, the filestream is at the + end of the PLY header. + */ + inline static void + readHeaderASCII(size_t& a_numVertices, size_t& a_numFaces, std::ifstream& a_inputStream); + + /*! + @brief Read ASCII vertices. + @param[out] a_vertices DCEL vertices. These are constructed in this routine. + @param[in] a_numVertices Number of vertices to read + @param[in] a_inputStream Input file stream. + @note The next getline() from a_inputStream must read the first vertex (i.e. + don't rewind the stream before entering this routine) + */ + inline static void + readVerticesIntoDCEL(std::vector>& a_vertices, + const size_t a_numVertices, + std::ifstream& a_inputStream); + + /*! + @brief Read ASCII faces and create mesh connectivity. + @param[out] a_faces DCEL faces. Constructured in this routine. + @param[out] a_edges DCEL edges. Constructured in this routine. + @param[out] a_vertices DCEL edges. Constructured in + readVerticesIntoDCEL. + @param[in] a_numFaces Total number of faces in mesh. + @param[in,out] a_inputStream Input stream + @note The next getline() from inputStream must read the first face, i.e. we + assume that read_ascii_vertices was called IMMEDIATELY before this function. + That function will center the fstream on the correct line in the input file. + */ + inline static void + readFacesIntoDCEL(std::vector>& a_faces, + std::vector>& a_edges, + const std::vector>& a_vertices, + const size_t a_numFaces, + std::ifstream& a_inputStream); + + /*! + @brief Reconcile pair edges, i.e. find the pair edge for every constructed + half-edge + @param[in,out] a_edges Half edges. + */ + inline static void + reconcilePairEdgesDCEL(std::vector>& a_edges); + }; } // namespace Parser #include "EBGeometry_NamespaceFooter.hpp" diff --git a/Source/EBGeometry_ParserImplem.hpp b/Source/EBGeometry_ParserImplem.hpp index 753a456f..48c146c3 100644 --- a/Source/EBGeometry_ParserImplem.hpp +++ b/Source/EBGeometry_ParserImplem.hpp @@ -46,8 +46,8 @@ Parser::PLY::readIntoDCEL(Mesh& a_mesh, const std::string a_filename) if (filestream.is_open()) { std::vector>& vertices = a_mesh.getVertices(); - std::vector>& edges = a_mesh.getEdges(); - std::vector>& faces = a_mesh.getFaces(); + std::vector>& edges = a_mesh.getEdges(); + std::vector>& faces = a_mesh.getFaces(); vertices.resize(0); edges.resize(0); @@ -66,7 +66,8 @@ Parser::PLY::readIntoDCEL(Mesh& a_mesh, const std::string a_filename) filestream.close(); a_mesh.reconcile(EBGeometry::DCEL::MeshT::VertexNormalWeight::Angle); - } else { + } + else { const std::string error = "Parser::PLY::readIntoDCEL - ERROR! Could not open file " + a_filename; std::cerr << error + "\n"; } @@ -117,19 +118,20 @@ Parser::PLY::readHeaderASCII(size_t& a_numVertices, size_t& a_numFaces, std:: template inline void -Parser::PLY::readVerticesIntoDCEL( - std::vector>& a_vertices, const size_t a_numVertices, std::ifstream& a_inputStream) +Parser::PLY::readVerticesIntoDCEL(std::vector>& a_vertices, + const size_t a_numVertices, + std::ifstream& a_inputStream) { Vec3T pos; - T& x = pos[0]; - T& y = pos[1]; - T& z = pos[2]; + T& x = pos[0]; + T& y = pos[1]; + T& z = pos[2]; Vec3T norm; - T& nx = norm[0]; - T& ny = norm[1]; - T& nz = norm[2]; + T& nx = norm[0]; + T& ny = norm[1]; + T& nz = norm[2]; size_t num = 0; @@ -150,18 +152,17 @@ Parser::PLY::readVerticesIntoDCEL( template inline void -Parser::PLY::readFacesIntoDCEL( - std::vector>& a_faces, - std::vector>& a_edges, - const std::vector>& a_vertices, - const size_t a_numFaces, - std::ifstream& a_inputStream) +Parser::PLY::readFacesIntoDCEL(std::vector>& a_faces, + std::vector>& a_edges, + const std::vector>& a_vertices, + const size_t a_numFaces, + std::ifstream& a_inputStream) { - size_t numVertices; + size_t numVertices; std::vector vertexIndices; std::string line; - size_t counter = 0; + size_t counter = 0; while (std::getline(a_inputStream, line)) { counter++; @@ -198,7 +199,7 @@ Parser::PLY::readFacesIntoDCEL( // Associate next/previous for the half edges inside the current face. Wish // we had a circular iterator but this will have to do. for (size_t i = 0; i < halfEdges.size(); i++) { - auto& curEdge = halfEdges[i]; + auto& curEdge = halfEdges[i]; auto& nextEdge = halfEdges[(i + 1) % halfEdges.size()]; curEdge->setNextEdge(nextEdge); @@ -233,15 +234,15 @@ Parser::PLY::reconcilePairEdgesDCEL(std::vector>& a_edg const auto& nextEdge = curEdge->getNextEdge(); const auto& vertexStart = curEdge->getVertex(); - const auto& vertexEnd = nextEdge->getVertex(); + const auto& vertexEnd = nextEdge->getVertex(); for (const auto& p : vertexStart->getFaces()) { for (EdgeIterator edgeIt(*p); edgeIt.ok(); ++edgeIt) { - const auto& polyCurEdge = edgeIt(); + const auto& polyCurEdge = edgeIt(); const auto& polyNextEdge = polyCurEdge->getNextEdge(); const auto& polyVertexStart = polyCurEdge->getVertex(); - const auto& polyVertexEnd = polyNextEdge->getVertex(); + const auto& polyVertexEnd = polyNextEdge->getVertex(); if (vertexStart == polyVertexEnd && polyVertexStart == vertexEnd) { // Found the pair edge curEdge->setPairEdge(polyCurEdge); diff --git a/Source/EBGeometry_Polygon2DImplem.hpp b/Source/EBGeometry_Polygon2DImplem.hpp index e2d11dba..f97aec3e 100644 --- a/Source/EBGeometry_Polygon2DImplem.hpp +++ b/Source/EBGeometry_Polygon2DImplem.hpp @@ -113,7 +113,8 @@ Polygon2D::computeWindingNumber(const Vec2& P) const noexcept if (P2.y > P.y) // an upward crossing if (res > 0.) // P left of edge ++wn; // have a valid up intersect - } else { // start y > P.y (no test needed) + } + else { // start y > P.y (no test needed) if (P2.y <= P.y) // a downward crossing if (res < 0.) // P right of edge --wn; // have a valid down intersect @@ -135,7 +136,7 @@ Polygon2D::computeCrossingNumber(const Vec2& P) const noexcept const Vec2& P1 = m_points[i]; const Vec2& P2 = m_points[(i + 1) % N]; - const bool upwardCrossing = (P1.y <= P.y) && (P2.y > P.y); + const bool upwardCrossing = (P1.y <= P.y) && (P2.y > P.y); const bool downwardCrossing = (P1.y > P.y) && (P2.y <= P.y); if (upwardCrossing || downwardCrossing) { diff --git a/Source/EBGeometry_TransformOpsImplem.hpp b/Source/EBGeometry_TransformOpsImplem.hpp index 5c2a424c..5dda8132 100644 --- a/Source/EBGeometry_TransformOpsImplem.hpp +++ b/Source/EBGeometry_TransformOpsImplem.hpp @@ -38,7 +38,7 @@ TranslateOp::transform(const Vec3T& a_inputPoint) const noexcept template RotateOp::RotateOp() { - m_axis = Vec3T::unit(); + m_axis = Vec3T::unit(); m_cosAngle = std::cos(T(0.0)); m_sinAngle = std::sin(T(0.0)); } @@ -46,7 +46,7 @@ RotateOp::RotateOp() template RotateOp::RotateOp(const T a_angle, const size_t a_axis) noexcept { - m_axis = a_axis; + m_axis = a_axis; m_cosAngle = std::cos(a_angle); m_sinAngle = std::sin(a_angle); } diff --git a/Source/EBGeometry_UnionBVH.hpp b/Source/EBGeometry_UnionBVH.hpp index 48ff0f15..f60f8917 100644 --- a/Source/EBGeometry_UnionBVH.hpp +++ b/Source/EBGeometry_UnionBVH.hpp @@ -58,10 +58,9 @@ class UnionBVH : public SignedDistanceFunction @param[in] a_flipSign Hook for turning inside to outside @param[in] a_bvConstructor Bounding volume constructor. */ - UnionBVH( - const std::vector>& a_distanceFunctions, - const bool a_flipSign, - const BVConstructor& a_bvConstructor); + UnionBVH(const std::vector>& a_distanceFunctions, + const bool a_flipSign, + const BVConstructor& a_bvConstructor); /*! @brief Build BVH tree for the input objects. User must supply a partitioner diff --git a/Source/EBGeometry_UnionBVHImplem.hpp b/Source/EBGeometry_UnionBVHImplem.hpp index f4aeb7e4..c0e571dd 100644 --- a/Source/EBGeometry_UnionBVHImplem.hpp +++ b/Source/EBGeometry_UnionBVHImplem.hpp @@ -24,14 +24,13 @@ UnionBVH::UnionBVH(const std::vector>& a_distance } m_flipSign = a_flipSign; - m_isGood = false; + m_isGood = false; } template -UnionBVH::UnionBVH( - const std::vector>& a_distanceFunctions, - const bool a_flipSign, - const BVConstructor& a_bvConstructor) +UnionBVH::UnionBVH(const std::vector>& a_distanceFunctions, + const bool a_flipSign, + const BVConstructor& a_bvConstructor) : UnionBVH(a_distanceFunctions, a_flipSign) { @@ -80,8 +79,8 @@ UnionBVH::buildTree(const BVConstructor& a_bvConstructor) // volumes. We do this by packing the SDFs and their BV centroids // in a vector which we sort (I love C++). using Primitive = std::shared_ptr; - using Centroid = Vec3T; - using PC = std::pair; + using Centroid = Vec3T; + using PC = std::pair; // Vector pack. std::vector primsAndCentroids; @@ -104,12 +103,12 @@ UnionBVH::buildTree(const BVConstructor& a_bvConstructor) // 4. Figure out where along the PC vector we should do our spatial splits. // We try to balance the chunks. const size_t almostEqualChunkSize = numPrimitives / K; - size_t remainder = numPrimitives % K; + size_t remainder = numPrimitives % K; std::array startIndices; std::array endIndices; - startIndices[0] = 0; + startIndices[0] = 0; endIndices[K - 1] = numPrimitives; for (size_t i = 1; i < K; i++) { diff --git a/Source/EBGeometry_Vec.hpp b/Source/EBGeometry_Vec.hpp index 9245ff48..e316c67d 100644 --- a/Source/EBGeometry_Vec.hpp +++ b/Source/EBGeometry_Vec.hpp @@ -235,8 +235,8 @@ class Vec3T { public: /*! - @brief Default constructor. Sets the vector to the zero vector. -*/ + @brief Default constructor. Sets the vector to the zero vector. + */ Vec3T(); /*! diff --git a/Source/EBGeometry_VecImplem.hpp b/Source/EBGeometry_VecImplem.hpp index 156652e8..ddbd683d 100644 --- a/Source/EBGeometry_VecImplem.hpp +++ b/Source/EBGeometry_VecImplem.hpp @@ -239,7 +239,7 @@ inline constexpr Vec3T Vec3T::unit(const size_t a_dir) noexcept { Vec3T v = Vec3T::zero(); - v[a_dir] = 1.0; + v[a_dir] = 1.0; return v; } @@ -421,7 +421,8 @@ Vec3T::minDir(const bool a_doAbs) const noexcept if (std::abs(X[dir]) < std::abs(X[mDir])) { mDir = dir; } - } else { + } + else { if (X[dir] < X[mDir]) { mDir = dir; } @@ -442,7 +443,8 @@ Vec3T::maxDir(const bool a_doAbs) const noexcept if (std::abs(X[dir]) > std::abs(X[mDir])) { mDir = dir; } - } else { + } + else { if (X[dir] > X[mDir]) { mDir = dir; } diff --git a/Sphinx/source/Example_AMReX.rst b/Sphinx/source/Example_AMReX.rst index 51f014bf..80af6db0 100644 --- a/Sphinx/source/Example_AMReX.rst +++ b/Sphinx/source/Example_AMReX.rst @@ -9,17 +9,11 @@ We will focus on the following parts of the code: .. literalinclude:: ../../Examples/AMReX_DCEL/main.cpp :language: c++ - :lines: 18-28, 32,37,42-43,49-63, 76-81, 85-88, 101-102 Constructing the BVH -------------------- -When constructing the signed distance function we use the DCEL and BVH functionality directly in the constructor: - -.. literalinclude:: ../../Examples/AMReX_DCEL/main.cpp - :language: c++ - :lines: 49,52,55,56-58,61-62 - +When constructing the signed distance function we use the DCEL and BVH functionality directly in the constructor. Note that we are performing the following steps: * Using the PLY parser for creating a DCEL mesh. @@ -31,8 +25,8 @@ Exposing signed distance functions Next, we expose the signed distance function to AMReX by implementing the functions -.. literalinclude:: ../../Examples/AMReX_DCEL/main.cpp - :language: c++ - :lines: 76-81, 85-87 +.. code-block:: c++ + + Real operator()(AMREX_D_DECL(Real x, Real y, Real z)) const noexcept Note that the AMReX ``DECL`` macros expand to ``(Real x, Real y)`` in 2D, but here we assume that the user has compiled for 3D. diff --git a/Sphinx/source/Example_Chombo3.rst b/Sphinx/source/Example_Chombo3.rst index 60557a21..62c4156e 100644 --- a/Sphinx/source/Example_Chombo3.rst +++ b/Sphinx/source/Example_Chombo3.rst @@ -9,17 +9,11 @@ We will focus on the following parts of the code: .. literalinclude:: ../../Examples/Chombo3_DCEL/main.cpp :language: c++ - :lines: 22-25, 29-30, 34-35, 38,40,43-46,49,50, 55-64, 73 Constructing the BVH -------------------- -When constructing the signed distance function we use the DCEL and BVH functionality directly in the constructor: - -.. literalinclude:: ../../Examples/Chombo3_DCEL/main.cpp - :language: c++ - :lines: 38-50 - +When constructing the signed distance function we use the DCEL and BVH functionality directly in the constructor. Note that we are performing the following steps: * Using the PLY parser for creating a DCEL mesh. @@ -31,8 +25,20 @@ Exposing signed distance functions Next, we expose the signed distance function to Chombo3 by implementing the functions -.. literalinclude:: ../../Examples/Chombo3_DCEL/main.cpp - :language: c++ - :lines: 56-64 +.. code-block:: c++ + + Real + value(const RealVect& a_point) const override final + { + #if CH_SPACEDIM == 2 + Vec3 p(a_point[0], a_point[1], 0.0); + #else + Vec3 p(a_point[0], a_point[1], a_point[2]); + #endif + + return Real(m_rootNode->signedDistance(p)); + } + + Note that because Chombo3 can be compiled in either 2D or 3D, we put a Chombo preprocessor flag ``CH_SPACEDIM`` in order to translate the Chombo ``RealVect`` to EBGeometry's inherent 3D vector structure. diff --git a/Sphinx/source/ImplemBVH.rst b/Sphinx/source/ImplemBVH.rst index d3846077..f234796c 100644 --- a/Sphinx/source/ImplemBVH.rst +++ b/Sphinx/source/ImplemBVH.rst @@ -28,11 +28,17 @@ The above template parameters are: Importantly, ``NodeT`` is the BVH builder node, i.e. it is the class through which we recursively build the BVH, see :ref:`Chap:BVHConstruction`. The compact BVH is discussed below in :ref:`Chap:LinearBVH`. -For getting the signed distance, ``NodeT`` has provides the following function: +For getting the signed distance, ``NodeT`` has provide the following functions: -.. literalinclude:: ../../Source/EBGeometry_BVH.hpp - :language: c++ - :lines: 112-115,220-221,330 +.. code-block:: c++ + + inline T + signedDistance(const Vec3T& a_point) const noexcept override; + + inline T + signedDistance(const Vec3T& a_point, const Prune a_pruning) const noexcept; + +The first version simply calls the other version with a stack-based pruning algorithm for the tree traversal. .. _Chap:BVHConstraints: @@ -163,9 +169,31 @@ Although seemingly complicated, the input arguments are simply polymorphic funct The function takes an list of primitives which it partitions into ``K`` new list of primitives, i.e. it encapsulates :eq:`Partition`. As an example, we include a partitioner that is provided for integrating BVH and DCEL functionality. - .. literalinclude:: ../../Source/EBGeometry_DcelBVH.hpp - :language: c++ - :lines: 100-121 + .. code-block:: c++ + + template + EBGeometry::BVH::PartitionerT, BV, K> chunkPartitioner = + [](const PrimitiveList& a_primitives) -> std::array, K> { + Vec3T lo = Vec3T::max(); + Vec3T hi = -Vec3T::max(); + for (const auto& p : a_primitives) { + lo = min(lo, p->getCentroid()); + hi = max(hi, p->getCentroid()); + } + + const size_t splitDir = (hi - lo).maxDir(true); + + // Sort the primitives along the above coordinate direction. + PrimitiveList sortedPrimitives(a_primitives); + + std::sort( + sortedPrimitives.begin(), sortedPrimitives.end(), + [splitDir](const std::shared_ptr>& f1, const std::shared_ptr>& f2) -> bool { + return f1->getCentroid(splitDir) < f2->getCentroid(splitDir); + }); + + return EBGeometry::DCEL::equalCounts(sortedPrimitives); + }; In the above, we are taking a list of DCEL facets in the input argument (``PrimitiveList`` expands to ``std::vector >``). We then compute the centroid locations of each facet and figure out along which coordinate axis we partition the objects (called ``splitDir`` above). @@ -199,9 +227,10 @@ To encapsulate the compact BVH we provide two classes: * ``LinearNodeT`` which encapsulates a node, but rather than storing the primitives and pointers to child nodes it stores offsets along the 1D arrays. Just like ``NodeT`` the class is templated: - .. literalinclude:: ../../Source/EBGeometry_BVH.hpp - :language: c++ - :lines: 352-354, 473 + .. code-block:: c++ + + template + class LinearNodeT ``LinearNodeT`` has a smaller memory footprint and should fit in one CPU word in floating-point precision and two CPU words in double point precision. The performance benefits of further memory alignment have not been investigated. @@ -219,9 +248,18 @@ To encapsulate the compact BVH we provide two classes: * ``LinearBVH`` which stores the compact BVH *and* primitives as class members. That is, ``LinearBVH`` contains the nodes and primitives as class members. - .. literalinclude:: ../../Source/EBGeometry_BVH.hpp - :language: c++ - :lines: 477-480,529-533,537,544 + .. code-block:: c++ + + template + class LinearBVH : public SignedDistanceFunction + { + public: + + protected: + + std::vector>> m_linearNodes; + std::vector> m_primitives; + }; The root node is, of course, found at the front of the ``m_linearNodes`` vector. Note that the list of primitives ``m_primitives`` is stored in the order in which the leaf nodes appear in ``m_linearNodes``. @@ -229,9 +267,16 @@ To encapsulate the compact BVH we provide two classes: Constructing the compact BVH is simply a matter of letting ``NodeT`` aggregate the nodes and primitives into arrays, and return a ``LinearBVH``. This is done by calling the ``NodeT`` member function ``flattenTree()``: -.. literalinclude:: ../../Source/EBGeometry_BVH.hpp - :language: c++ - :lines: 112-114,236-237,329 +.. code-block:: c++ + + template + class NodeT : public SignedDistanceFunction + { + public: + + inline std::shared_ptr> + flattenTree() const noexcept; + }; which returns a pointer to a ``LinearBVH``. For example: @@ -255,9 +300,16 @@ For example: Note that the primitives live in ``LinearBVH`` and not ``LinearNodeT``, and the signed distance function is therefore implemented in the ``LinearBVH`` member function: -.. literalinclude:: ../../Source/EBGeometry_BVH.hpp - :language: c++ - :lines: 477-479, 529-530, 544 +.. code-block:: c++ + + template + class LinearBVH : public SignedDistanceFunction + { + public: + + inline T + signedDistance(const Vec3& a_point) const noexcept override; + }; Signed distance --------------- diff --git a/Sphinx/source/ImplemDCEL.rst b/Sphinx/source/ImplemDCEL.rst index 3e3ae68b..786f970f 100644 --- a/Sphinx/source/ImplemDCEL.rst +++ b/Sphinx/source/ImplemDCEL.rst @@ -3,7 +3,7 @@ DCEL ==== -The DCEL functionality exists under the namespace ``EBGeometry::Dcel`` and contains the following functionality: +The DCEL functionality exists under the namespace ``EBGeometry::DCEL`` and contains the following functionality: * **Fundamental data types** like vertices, half-edges, polygons, and entire surface grids. * **Signed distance functionality** for the above types. @@ -19,33 +19,36 @@ Classes The main DCEL functionality (vertices, edges, faces) is provided by the following classes: -* **Vertices** are implemented as a template ``EBGeometry::Dcel::EdgeT`` +* **Vertices** are implemented as a template ``EBGeometry::DCEL::EdgeT`` - .. literalinclude:: ../../Source/EBGeometry_DcelVertex.hpp - :language: c++ - :lines: 41-43 + .. code-block:: c++ + + template + class VertexT The DCEL vertex class stores the vertex position, normal vector, and the outgoing half-edge from the vertex. Note that the class has member functions for computing the vertex pseudonormal, see :ref:`Chap:NormalDCEL`. - The full API is given in the doxygen documentation `here `__. + The full API is given in the doxygen documentation `here `__. -* **Edges** are implemented as a template ``EBGeometry::Dcel::EdgeT`` +* **Edges** are implemented as a template ``EBGeometry::DCEL::EdgeT`` - .. literalinclude:: ../../Source/EBGeometry_DcelEdge.hpp - :language: c++ - :lines: 43-45 + .. code-block:: c++ + + template + class EdgeT The half-edges store a reference to their face, as well as pointers to the previous edge, next edge, pair edge, and starting vertex. For performance reasons, the edge also stores the length and inverse length of the edge. - The full API is given in the doxygen documentation `here `__. + The full API is given in the doxygen documentation `here `__. -* **Faces** are implemented as a template ``EBGeometry::Dcel::FaceT`` +* **Faces** are implemented as a template ``EBGeometry::DCEL::FaceT`` - .. literalinclude:: ../../Source/EBGeometry_DcelFace.hpp - :language: c++ - :lines: 44-46 + .. code-block:: c++ + + template + class FaceT For performance reasons, a polygon face stores all it's half-edges (to avoid iteration when computing the signed distance). It also stores: @@ -57,19 +60,20 @@ The main DCEL functionality (vertices, edges, faces) is provided by the followin The normal vector and 2D embedding of the facet exist because the signed distance computation requires them. The centroid position exists only because BVH partitioners will use it for partitioning the surface mesh. - The full API is given in the doxygen documentation `here `__. + The full API is given in the doxygen documentation `here `__. -* **Mesh** is implemented as a template ``EBGeometry::Dcel::MeshT`` +* **Mesh** is implemented as a template ``EBGeometry::DCEL::MeshT`` - .. literalinclude:: ../../Source/EBGeometry_DcelMesh.hpp - :language: c++ - :lines: 42-44 + .. code-block:: c++ + + template + class MeshT The Mesh stores all the vertices, half-edges, and faces. - For example, to obtain all the facets one can call ``EBGeometry::Dcel::MeshT::getFaces()`` which will return all the DCEL faces of the surface mesh. + For example, to obtain all the facets one can call ``EBGeometry::DCEL::MeshT::getFaces()`` which will return all the DCEL faces of the surface mesh. Typically, the mesh is not created by the user but automatically created when reading the mesh from an input file. - The full API is given in the doxygen documentation `here `__. + The full API is given in the doxygen documentation `here `__. All of the above DCEL classes have member functions of the type: @@ -81,36 +85,6 @@ All of the above DCEL classes have member functions of the type: Thus, they fulfill the template requirements of the primitive type for the BVH implementation, see :ref:`Chap:BVHConstraints`. See :ref:`Chap:BVHIntegration` for details regarding DCEL integration with BVHs. - -File parsers ------------- - -Routines for parsing surface files from grids into EBGeometry's DCEL grids are given in the namespace ``EBGeometry::Dcel::Parser``. -Currently, this is limited to the following file formats: - -* **PLY** Only ASCII formats currently supported, ``_. - - When reading a PLY file into the DCEL data structures, it is sufficient to call the following static function: - - .. code-block:: - - template - using Mesh = EBGeometry::Dcel::MeshT; - - template - std::shared_ptr EBGeometry::Parser::PLY::readASCII(const std::string a_filename); - - .. warning:: - - Although the parser will do it's best to read files that contains holes or incomplete faces, success will fluctuate. - Moreover, the signed distance function is not well-defined for such cases. - - Calling the ``readASCII`` function will read the input file (which is assumed to be a PLY file) and create the DCEL data structures. - -.. note:: - - If the file format of your surface mesh file is not one of the above, consider either providing a new plugin or convert your file (e.g. to PLY) using MeshLab, Blender, etc. - .. _Chap:BVHIntegration: BVH integration @@ -120,42 +94,70 @@ DCEL functionality can easily be embedded in BVHs. In this case it is the facets that are embedded in the BVHs, and we require that we can create bounding volumes that contain all the vertices in a facet. Moreover, partitioning functions that partition a set of polygon faces into ``K`` new sets of faces are also required. -Construction methods -____________________ - EBGeometry provides some simplistic functions that are needed (see :ref:`Chap:BVHConstruction`) when building BVHs for DCEL geometries . .. note:: - The functions are defined in :file:`Source/EBGeometry_DcelBVH.hpp`. + The functions are defined in :file:`Source/EBGeometry_DCEL_BVH.hpp`. For the bounding volume constructor, we provide a function -.. literalinclude:: ../../Source/EBGeometry_DcelBVH.hpp - :language: c++ - :lines: 40-43 +.. code-block:: c++ + + template + EBGeometry::BVH::BVConstructorT, BV> defaultBVConstructor = + [](const std::shared_ptr>& a_primitive) -> BV { + return BV(a_primitive->getAllVertexCoordinates()); + }; -Note the extra template constraint on the bounding volume type ``BV``, which must be able to construct a bounding volume from a finite point set (the vertex coordinates). +Note the extra template constraint on the bounding volume type ``BV``, which must be able to construct a bounding volume from a finite point set (in this case the vertex coordinates). For the stop function we provide a simple function -.. literalinclude:: ../../Source/EBGeometry_DcelBVH.hpp - :language: c++ - :lines: 52-55 +.. code-block:: c++ + + template + EBGeometry::BVH::StopFunctionT, BV, K> defaultStopFunction = + [](const BVH::NodeT, BV, K>& a_node) -> bool { + return (a_node.getPrimitives()).size() < K; + }; Note that this simply terminates the leaf partitioning if there are not enough primitives (polygon faces) available, or there are fewer than a pre-defined number of primitives. -For the partitioning function we include a simple function that partitions the primites along the longest axis: +For the partitioning function we include a simple function that partitions the primitives along the longest axis: + +.. code-block:: c++ -.. literalinclude:: ../../Source/EBGeometry_DcelBVH.hpp - :language: c++ - :lines: 221-222 + template + EBGeometry::BVH::PartitionerT, BV, K> chunkPartitioner = + [](const PrimitiveList& a_primitives) -> std::array, K> { + Vec3T lo = Vec3T::max(); + Vec3T hi = -Vec3T::max(); + + for (const auto& p : a_primitives) { + lo = min(lo, p->getCentroid()); + hi = max(hi, p->getCentroid()); + } -Note that this function is just an alias for a different partitioner which splits the bounding volumes into equal-sized chunks. -See :file:`Source/EBGeometry_DcelBVH.hpp` for details. + const size_t splitDir = (hi - lo).maxDir(true); + + // Sort the primitives along the above coordinate direction. + PrimitiveList sortedPrimitives(a_primitives); + + std::sort( + sortedPrimitives.begin(), sortedPrimitives.end(), + [splitDir](const std::shared_ptr>& f1, const std::shared_ptr>& f2) -> bool { + return f1->getCentroid(splitDir) < f2->getCentroid(splitDir); + }); + + return EBGeometry::DCEL::equalCounts(sortedPrimitives); + }; + + +For a list of all DCEL partitioner, see :file:`Source/EBGeometry_DCEL_BVH.hpp`. Code example -____________ +------------ Constructing a compact BVH representation of polygon mesh is therefore done as follows: @@ -164,20 +166,20 @@ Constructing a compact BVH representation of polygon mesh is therefore done as f using T = float; using BV = EBGeometry::BoundingVolumes::AABBT; using Vec3 = EBGeometry::Vec3T; - using Face = EBGeometry::Dcel::FaceT; + using Face = EBGeometry::DCEL::FaceT; constexpr int K = 4; // Read the mesh from file and put it in a DCEL format. - std::shared_ptr > mesh = EBGeometry::Dcel::Parser::Ply("MyFile.ply"); + std::shared_ptr > mesh = EBGeometry::Parser::PLY("MyFile.ply"); // Make a BVH node and build the BVH. auto root = std::make_shared >(mesh->getFaces()); // Build the BVH hierarchy - root->topDownSortAndPartitionPrimitives(EBGeometry::Dcel::defaultBVConstructor, - EBGeometry::Dcel::spatialSplitPartitioner, - EBGeometry::Dcel::defaultStopFunction); + root->topDownSortAndPartitionPrimitives(EBGeometry::DCEL::defaultBVConstructor, + EBGeometry::DCEL::spatialSplitPartitioner, + EBGeometry::DCEL::defaultStopFunction); // Flatten the tree onto a tighter representation. Then delete the old tree. auto compactBVH = root->flattenTree(); diff --git a/Sphinx/source/ImplemSDF.rst b/Sphinx/source/ImplemSDF.rst index 3217af79..79dc77cd 100644 --- a/Sphinx/source/ImplemSDF.rst +++ b/Sphinx/source/ImplemSDF.rst @@ -5,12 +5,11 @@ Signed distance function In EBGeometry we have encapsulated the concept of a signed distance function in an abstract class -.. literalinclude:: ../../Source/EBGeometry_SignedDistanceFunction.hpp +.. literalinclude:: ../../Source/EBGeometry_SignedDistanceFunction.hpp :language: c++ - :lines: 28-31, 46-47, 54-56, 61-63, 69-71, 82-84 -We point out that the BVH and DCEL functionalities are fundamentally also signed distance functions, even though they do not inherent from ``EBGeometry::SignedDistanceFunction``. -The ``SignedDistanceFunction`` class exists so that we have a common entry point for performing distance field manipulations like rotations and translations. +We point out that the BVH and DCEL classes are fundamentally also signed distance functions, and they also inherit from ``SignedDistanceFunction``. +The ``SignedDistanceFunction`` class also exists so that we have a common entry point for performing distance field manipulations like rotations and translations. When implementing the ``signedDistance`` function, one can transform the input point by first calling ``transformPoint``. The functions ``translate`` and ``rotate`` will translate or rotate the object. @@ -105,7 +104,7 @@ The rounded SDF is implemented in :file:`Source/EBGeometry_AnalyticDistanceFunct .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp :language: c++ - :lines: 40-43, 54-58, 69-71, 84 + :lines: 45-89 To use it, simply pass an SDF into the constructor and use the new distance function. @@ -125,8 +124,7 @@ The rounded SDF is implemented in :file:`Source/EBGeometry_AnalyticDistanceFunct .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp :language: c++ - :lines: 138-151, 152-155, 168-170, 183 - + :lines: 143-188 .. _Chap:AnalyticSDF: @@ -138,49 +136,56 @@ In addition, the file :file:`Source/EBGeometry_AnalyticSignedDistanceFunctions.h * **Sphere** - .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp - :language: c++ - :lines: 239-240 + .. code-block:: c++ + + template + class SphereSDF : public SignedDistanceFunction * **Box** - .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp - :language: c++ - :lines: 334-335 + .. code-block:: c++ + + template + class BoxSDF : public SignedDistanceFunction * **Torus** - .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp - :language: c++ - :lines: 440-441 + .. code-block:: c++ + + template + class TorusSDF : public SignedDistanceFunction * **Capped cylinder** - .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp - :language: c++ - :lines: 560-561 + .. code-block:: c++ + + template + class CylinderSDF : public SignedDistanceFunction * **Infinite cylinder** - .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp - :language: c++ - :lines: 699-700 + .. code-block:: c++ + + template + class InfiniteCylinderSDF : public SignedDistanceFunction * **Capsule/rounded cylinder** - .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp - :language: c++ - :lines: 762-763 + .. code-block:: c++ + + template + class CapsuleSDF : public SignedDistanceFunction * **Infinite cone** - .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp - :language: c++ - :lines: 827-828 + .. code-block:: c++ + + template + class InfiniteConeSDF : public SignedDistanceFunction * **Cone** - .. literalinclude:: ../../Source/EBGeometry_AnalyticDistanceFunctions.hpp - :language: c++ - :lines: 895-896 + .. code-block:: c++ + template + class ConeSDF : public SignedDistanceFunction diff --git a/Sphinx/source/ImplemUnion.rst b/Sphinx/source/ImplemUnion.rst index 4f9ee26d..b839ea3c 100644 --- a/Sphinx/source/ImplemUnion.rst +++ b/Sphinx/source/ImplemUnion.rst @@ -16,14 +16,14 @@ The standard union is template as .. literalinclude:: ../../Source/EBGeometry_Union.hpp :language: c++ - :lines: 26-29,33-34,45 + :lines: 27-70 Note that ``EBGeometry::Union`` inherits from ``EBGeometry::SignedDistanceFunction`` and thus provides a ``signedDistance(...)`` function. The implementation of the standard union is .. literalinclude:: ../../Source/EBGeometry_UnionImplem.hpp :language: c++ - :lines: 28-43 + :lines: 20-44 That is, it iterates through *all* the objects in order to find the signed distance. @@ -34,7 +34,7 @@ The BVH-enabled union is implemented by ``EBGeometry::UnionBVH`` as follows: .. literalinclude:: ../../Source/EBGeometry_UnionBVH.hpp :language: c++ - :lines: 26-29,33-34,38-39,58-61,77-80, 94-95, 104, 115 + :lines: 27-122 As always, the template parameter ``T`` indicates the precision, ``BV`` the bounding volume type and ``K`` the tree degree. ``UnionBVH`` takes a bounding volume constructor in addition to the list of primitives, see :ref:`Chap:BVHConstruction`. @@ -46,6 +46,6 @@ The implementation of the signed distance function for the BVH-enabled union is .. literalinclude:: ../../Source/EBGeometry_UnionBVHImplem.hpp :language: c++ - :lines: 144-149 + :lines: 163-170 That is, it relies on pruning from the BVH functionality for finding the signed distance to the closest object. diff --git a/Sphinx/source/Implementation.rst b/Sphinx/source/Implementation.rst index 90d3e443..8a5958be 100644 --- a/Sphinx/source/Implementation.rst +++ b/Sphinx/source/Implementation.rst @@ -5,7 +5,7 @@ Overview Here, we consider the basic EBGeometry API. EBGeometry is a header-only library, implemented under it's own namespace ``EBGeometry``. -Various major components, like BVHs and DCEL, are implemented under namespaces ``EBGeometry::BVH`` and ``EBGeometry::Dcel``. +Various major components, like BVHs and DCEL, are implemented under namespaces ``EBGeometry::BVH`` and ``EBGeometry::DCEL``. Below, we consider a brief introduction to the API and implementation details of EBGeometry. diff --git a/Sphinx/source/Introduction.rst b/Sphinx/source/Introduction.rst index c925a2f1..572a2062 100644 --- a/Sphinx/source/Introduction.rst +++ b/Sphinx/source/Introduction.rst @@ -7,7 +7,6 @@ Requirements ============ * A C++ compiler which supports C++14. -* An analytic signed distance function, or a watertight and orientable surface grid. Quickstart ========== @@ -18,14 +17,23 @@ To obtained EBGeometry, clone the code from `github `_. + + When reading a PLY file into the DCEL data structures, it is sufficient to call the following static function: + + .. code-block:: + + template + using Mesh = EBGeometry::DCEL::MeshT; + + template + std::shared_ptr EBGeometry::Parser::PLY::readASCII(const std::string a_filename); + + .. warning:: + + Although the parser will do it's best to read files that contains holes or incomplete faces, success will fluctuate. + Moreover, the signed distance function is not well-defined for such cases. + + Calling the ``readASCII`` function will read the input file (which is assumed to be a PLY file) and create the DCEL data structures. + +.. note:: + + If the file format of your surface mesh file is not one of the above, consider either providing a new plugin or convert your file (e.g. to PLY) using MeshLab, Blender, etc. diff --git a/Sphinx/source/index.rst b/Sphinx/source/index.rst index 1d3748f0..0506c426 100644 --- a/Sphinx/source/index.rst +++ b/Sphinx/source/index.rst @@ -8,13 +8,14 @@ EBGeometry's user documentation This is the user documentation for EBGeometry, a small C++ package for computing signed distance fields from surface tesselations and analytic shapes. Although EBGeometry is a self-contained package, it is was originally written for usage with embedded boundary (EB) and immersed boundary (IB) codes. -EBGeometry does not involve itself with the *discrete geometry generation*, i.e. the generation of cut-cells. + +EBGeometry does provide the *discrete geometry generation*, i.e. the generation of cut-cells from a geometry. It only takes care of the *geometry representation*, i.e. the creation of complex geometries as numerically efficient signed distance fields. .. important:: The EBGeometry source code is found `here `_. - A separate Doxygen-generated API of EBGeometry is available `here `_. + A separate Doxygen-generated API of EBGeometry is `available here `_. .. This is for getting rid of the TOC in html view. .. raw:: html @@ -68,7 +69,8 @@ Implementation ImplemBVH.rst ImplemDCEL.rst ImplemSDF.rst - ImplemUnion.rst + ImplemUnion.rst + Parsers.rst Guided examples ***************