// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2015 Alec Jacobson // // This Source Code Form is subject to the terms of the Mozilla Public License // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at http://mozilla.org/MPL/2.0/. #ifndef IGL_AABB_H #define IGL_AABB_H #include "Hit.h" #include "igl_inline.h" #include #include #include namespace igl { /// Implementation of semi-general purpose axis-aligned bounding box hierarchy. /// The mesh (V,Ele) is stored and managed by the caller and each routine here /// simply takes it as references (it better not change between calls). /// /// It's a little annoying that the Dimension is a template parameter and not /// picked up at run time from V. This leads to duplicated code for 2d/3d (up to /// dim). /// /// @tparam DerivedV Matrix type of vertex positions (e.g., `Eigen::MatrixXd`) /// @tparam DIM Dimension of mesh vertex positions (2 or 3) template class AABB { public: /////////////////////////////////////////////////////////////////////////////// // Member variables /////////////////////////////////////////////////////////////////////////////// /// Scalar type of vertex positions (e.g., `double`) typedef typename DerivedV::Scalar Scalar; /// Fixed-size (`DIM`) RowVector type using `Scalar` typedef Eigen::Matrix RowVectorDIMS; /// Fixed-size (`DIM`) (Column)Vector type using `Scalar` typedef Eigen::Matrix VectorDIMS; /// Fixed-width (`DIM`) Matrix type using `Scalar` typedef Eigen::Matrix MatrixXDIMS; /// Pointer to "left" child node (`nullptr` if leaf) // Shared pointers are slower... AABB * m_left; /// Pointer to "right" child node (`nullptr` if leaf) AABB * m_right; /// Pointer to "parent" node (`nullptr` if root) AABB * m_parent; /// Axis-Aligned Bounding Box containing this node Eigen::AlignedBox m_box; /// Index of single primitive in this node if full leaf, otherwise -1 for non-leaf int m_primitive; /////////////////////////////////////////////////////////////////////////////// // Non-templated member functions /////////////////////////////////////////////////////////////////////////////// //Scalar m_low_sqr_d; //int m_depth; /// @private AABB(): m_left(nullptr), m_right(nullptr),m_parent(nullptr), m_box(), m_primitive(-1) //m_low_sqr_d(std::numeric_limits::infinity()), //m_depth(0) { static_assert(DerivedV::ColsAtCompileTime == DIM || DerivedV::ColsAtCompileTime == Eigen::Dynamic,"DerivedV::ColsAtCompileTime == DIM || DerivedV::ColsAtCompileTime == Eigen::Dynamic"); } /// @private // http://stackoverflow.com/a/3279550/148668 AABB(const AABB& other): m_left (other.m_left ? new AABB(*other.m_left) : nullptr), m_right(other.m_right ? new AABB(*other.m_right) : nullptr), m_parent(other.m_parent), m_box(other.m_box), m_primitive(other.m_primitive) //m_low_sqr_d(other.m_low_sqr_d), //m_depth(std::max( // m_left ? m_left->m_depth + 1 : 0, // m_right ? m_right->m_depth + 1 : 0)) { if(m_left) { m_left->m_parent = this; } if(m_right) { m_right->m_parent = this; } } /// @private // copy-swap idiom friend void swap(AABB& first, AABB& second) { // Enable ADL using std::swap; swap(first.m_left,second.m_left); swap(first.m_right,second.m_right); swap(first.m_parent,second.m_parent); swap(first.m_box,second.m_box); swap(first.m_primitive,second.m_primitive); //swap(first.m_low_sqr_d,second.m_low_sqr_d); //swap(first.m_depth,second.m_depth); } /// @private // Pass-by-value (aka copy) AABB& operator=(AABB other) { swap(*this,other); return *this; } /// @private AABB(AABB&& other): // initialize via default constructor AABB() { swap(*this,other); } /// @private // Seems like there should have been an elegant solution to this using // the copy-swap idiom above: IGL_INLINE void clear() { m_primitive = -1; m_box = Eigen::AlignedBox(); delete m_left; m_left = nullptr; delete m_right; m_right = nullptr; // Tell my parent I'm dead if(m_parent) { if(m_parent->m_left == this) { m_parent->m_left = nullptr; }else if(m_parent->m_right == this) { m_parent->m_right = nullptr; }else { assert(false && "I'm not my parent's child"); } auto * grandparent = m_parent->m_parent; if(grandparent) { // Before // grandparent // / \ // parent pibling // / \ // sibling this // // After // grandparent // / \ // sibling® pibling }else { // Before // parent=root // / \ // sibling this // // After // grandparent // / \ // sibling® pibling } } // Now my parent is dead to me. m_parent = nullptr; } /// @private ~AABB() { clear(); } /// Return whether at leaf node IGL_INLINE bool is_leaf() const; /// Return whether at root node IGL_INLINE bool is_root() const; /// Return the root node of this node's tree by following its parent IGL_INLINE AABB* root() const; IGL_INLINE AABB* detach(); IGL_INLINE void refit_lineage(); /// Get a vector of leaves indexed by their m_primitive id (these better /// be non-negative and tightly packed. /// @param[in] m number of leaves/elements (Ele.rows()) /// @returns leaves m list of pointers to leaves IGL_INLINE std::vector*> gather_leaves(const int m); /// \overload where m is the max m_primitive id in the tree. IGL_INLINE std::vector*> gather_leaves(); /// Pad leaves by `pad` in each dimension /// @param[in] pad padding amount /// @param[in] polish_rotate_passes number of passes to polish rotations /// @returns pointer to (potentially new) root IGL_INLINE AABB* pad( const std::vector*> & leaves, const Scalar pad, const int polish_rotate_passes=0); /// @returns `this` if no update was needed, otherwise returns pointer to /// (potentially new) root /// /// Example: /// ```cpp /// auto * up = leaf->update(new_box); /// if(up != leaf) /// { /// tree = up->root(); /// }else /// { /// printf("no update occurred\n"); /// } /// /// // or simply /// tree = leaf->update(new_box)->root(); /// ``` IGL_INLINE AABB* update( const Eigen::AlignedBox & new_box, const Scalar pad=0); /// Insert a (probably a leaf) AABB `other` into this AABB tree. If /// `other`'s box is contained in this AABB's box then insert it as a child recursively. /// /// If `other`'s box is not contained in this AABB's box then insert it as a /// sibling. /// /// It's a very good idea to call either `rotate` (faster, less good) or `rotate_lineage` (slower, better) /// after insertion. Rotating continues to improve the tree's quality so /// after doing a bunch of insertions you might even consider calling /// `rotate` on all nodes. /// /// `insert` attempts to minimize total internal surface area. Where as /// `init` is top-down and splits boxes based on the median along the /// longest dimension. When initializing a tree, `init` seems to result in /// great trees (small height and small total internal surface area). /// /// @param[in] other pointer to another AABB node /// @returns pointer to the parent of `other` or `other` itself. This /// could be == to a `new`ly created internal node or to `other` if /// `this==other`. Calling ->root() on this returned node will give you /// the root of the tree. /// /// ##### Example /// /// ```cpp /// // Create a tree (use pointer to track changes to root) /// auto * tree = new igl::AABB::AABB(); /// // Fill the tree (e.g., using ->init()) /// … /// // Create a new leafe node /// auto * leaf = new igl::AABB::AABB(); /// // Fill the leaf node with a primitive and box /// … /// // Insert into the tree and find the possibly new root /// tree = tree->insert(leaf)->root(); /// ``` IGL_INLINE AABB* insert(AABB * other); /// Insert `other` as a sibling to `this` by creating a new internal node /// to be their shared parent. /// /// Before /// parent /// / \ /// this(C) sibling /// / \ /// left right /// /// After /// parent /// / \ /// newbie sibling /// / \ /// this other /// / \ /// left right /// /// /// @param[in] other pointer to another AABB node /// @returns pointer to the new shared parent. IGL_INLINE AABB* insert_as_sibling(AABB * other); /// Try to swap this node with its close relatives if it will decrease /// total internal surface area. /// /// /// grandparent /// / \ /// parent pibling° /// / \ / \ /// sibling this cuz1° cuz2° /// / \ /// nib1° nib2° /// /// °Swap Candidates /// /// @param[in] dry_run if true then don't actually swap /// @return[in] the change in total internal surface area, 0 if no /// improvement and rotate won't be carried out. IGL_INLINE Scalar rotate(const bool dry_run = false); /// Try to swap this node with its cousins if it will decrease /// total internal surface area. /// /// @param[in] dry_run if true then don't actually swap /// @return[in] the change in total internal surface area, 0 if no /// improvement and rotate won't be carried out. /// /// Before /// grandparent /// / \ /// parent pibling /// / \ / \ /// sibling this cuz1 cuz2 /// /// /// Candidates /// grandparent /// / \ /// parent pibling /// / \ / \ /// sibling cuz1 this cuz2 /// /// Or /// grandparent /// / \ /// parent pibling /// / \ / \ /// sibling cuz2 cuz1 this IGL_INLINE Scalar rotate_across(const bool dry_run = false); /// Try to swap this node with its pibling if it will decrease /// total internal surface area. /// /// @param[in] dry_run if true then don't actually swap /// @return[in] the change in total internal surface area, 0 if no /// improvement and rotate won't be carried out. /// /// Before /// grandparent /// / \ /// other parent /// / \ /// this sibling /// /// /// Candidate /// grandparent /// / \ /// this parent /// / \ /// other sibling IGL_INLINE Scalar rotate_up(const bool dry_run = false); /// Try to swap this node with one of its niblings if it will decrease /// total internal surface area. /// /// @param[in] dry_run if true then don't actually swap /// @return[in] the change in total internal surface area, 0 if no /// improvement and rotate won't be carried out. /// /// Before /// parent /// / \ /// this sibling /// / \ /// left right /// /// /// Candidates /// parent /// / \ /// left sibling /// / \ /// this right /// /// Or /// /// parent /// / \ /// right sibling /// / \ /// left this IGL_INLINE Scalar rotate_down(const bool dry_run = false); /// "Rotate" (swap) `reining` with `challenger`. /// /// Before /// grandparent /// / \ /// reining parent /// / \ /// challenger sibling /// /// /// Candidate /// grandparent /// / \ /// challenger parent /// / \ /// reining sibling /// @param[in] reining pointer to AABB node to be rotated /// @param[in] grandparent pointer to challenger's grandparent /// @param[in] parent pointer to challenger's parent /// @param[in] challenger pointer to AABB node to be rotated /// @param[in] sibling pointer to challenger's sibling /// @returns true only if rotation was possible and successfully carried /// out. static IGL_INLINE Scalar rotate_up( const bool dry_run, AABB* reining, AABB* grandparent, AABB* parent, AABB* challenger, AABB* sibling); // Should this be a static function with an argument? IGL_INLINE void rotate_lineage(); /// Number of nodes contained in subtree (is it?) /// /// \note At best, this function has a dubious name. This is really an /// internal helper function for the serialization. /// /// \see size() /// /// @return Number of elements m then total tree size should be 2*h where h is /// the deepest depth 2^ceil(log(#Ele*2-1)) IGL_INLINE int subtree_size() const; /// @param[in] box query box /// @param[in,out] leaves list of leaves to append to IGL_INLINE bool append_intersecting_leaves( const Eigen::AlignedBox & box, std::vector*> & leaves) const; /// Compute sum of surface area of all internal (non-root, non-leaf) boxes IGL_INLINE typename DerivedV::Scalar internal_surface_area() const; /// Validate the subtree under this node by running a bunch of assertions. /// Does nothing when not in debug mode IGL_INLINE void validate() const; /// print the memory addresses of the tree in a somewhat legible way IGL_INLINE void print(const int depth = 0) const; /// @returns Actual size of tree. Total number of nodes in tree. A singleton root /// has size 1. /// /// \see subtree_size IGL_INLINE int size() const; /// @returns Height of the tree. A singleton root has height 1. IGL_INLINE int height() const; private: /// If new distance (sqr_d_candidate) is less than current distance /// (sqr_d), then update this distance and its associated values /// _in-place_: /// /// @param[in] p dim-long query point (only used in DEBUG mode) /// @param[in] sqr_d candidate minimum distance for this query, see /// output /// @param[in] i candidate index into Ele of closest point, see output /// @param[in] c dim-long candidate closest point, see output /// @param[in] sqr_d current minimum distance for this query, see output /// @param[in] i current index into Ele of closest point, see output /// @param[in] c dim-long current closest point, see output /// @param[out] sqr_d minimum of initial value and squared distance to /// this primitive /// @param[out] i possibly updated index into Ele of closest point /// @param[out] c dim-long possibly updated closest point IGL_INLINE void set_min( const RowVectorDIMS & p, const Scalar sqr_d_candidate, const int i_candidate, const RowVectorDIMS & c_candidate, Scalar & sqr_d, int & i, Eigen::PlainObjectBase & c) const; public: /////////////////////////////////////////////////////////////////////////////// // Templated member functions /////////////////////////////////////////////////////////////////////////////// /// Build an Axis-Aligned Bounding Box tree for a given mesh and given /// serialization of a previous AABB tree. /// /// @param[in] V #V by dim list of mesh vertex positions. /// @param[in] Ele #Ele by dim+1 list of mesh indices into #V. /// @param[in] bb_mins max_tree by dim list of bounding box min corner /// positions /// @param[in] bb_maxs max_tree by dim list of bounding box max corner /// positions /// @param[in] elements max_tree list of element or (not leaf id) indices /// into Ele /// @param[in] i recursive call index {0} template < typename DerivedEle, typename Derivedbb_mins, typename Derivedbb_maxs, typename Derivedelements> IGL_INLINE void init( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const Eigen::MatrixBase & bb_mins, const Eigen::MatrixBase & bb_maxs, const Eigen::MatrixBase & elements, const int i = 0); /// Build an Axis-Aligned Bounding Box tree for a given mesh and given /// serialization of a previous AABB tree. /// /// @param[in] V #V by dim list of mesh vertex positions. /// @param[in] Ele #Ele by dim+1 list of mesh indices into #V. template IGL_INLINE void init( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele); /// Build an Axis-Aligned Bounding Box tree for a given mesh. /// /// @param[in] V #V by dim list of mesh vertex positions. /// @param[in] Ele #Ele by dim+1 list of mesh indices into #V. /// @param[in] SI #Ele by dim list revealing for each coordinate where /// Ele's barycenters would be sorted: SI(e,d) = i --> the dth /// coordinate of the barycenter of the eth element would be placed at /// position i in a sorted list. /// @param[in] I #I list of indices into Ele of elements to include (for /// recursive calls) /// template IGL_INLINE void init( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const Eigen::MatrixBase & SI, const Eigen::MatrixBase& I); /// @returns `this` if no update was needed, otherwise returns pointer to /// (potentially new) root template IGL_INLINE AABB* update_primitive( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const Scalar pad=0); /// Find the indices of elements containing given point: this makes sense /// when Ele is a co-dimension 0 simplex (tets in 3D, triangles in 2D). /// /// @param[in] V #V by dim list of mesh vertex positions. **Should be /// same as used to construct mesh.** /// @param[in] Ele #Ele by dim+1 list of mesh indices into #V. **Should /// be same as used to construct mesh.** /// @param[in] q dim row-vector query position /// @param[in] first whether to only return first element containing q /// @return list of indices of elements containing q template IGL_INLINE std::vector find( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const Eigen::MatrixBase & q, const bool first=false) const; /// Serialize this class into 3 arrays (so we can pass it pack to matlab) /// /// @param[out] bb_mins max_tree by dim list of bounding box min corner /// positions /// @param[out] bb_maxs max_tree by dim list of bounding box max corner /// positions /// @param[out] elements max_tree list of element or (not leaf id) /// indices into Ele /// @param[in] i recursive call index into these arrays {0} template < typename Derivedbb_mins, typename Derivedbb_maxs, typename Derivedelements> IGL_INLINE void serialize( Eigen::PlainObjectBase & bb_mins, Eigen::PlainObjectBase & bb_maxs, Eigen::PlainObjectBase & elements, const int i = 0) const; /// Compute squared distance to a query point /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] p dim-long query point /// @param[out] i facet index corresponding to smallest distances /// @param[out] c closest point /// @return squared distance /// /// \pre Currently assumes Elements are triangles regardless of /// dimension. template IGL_INLINE Scalar squared_distance( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & p, int & i, Eigen::PlainObjectBase & c) const; /// Compute squared distance to a query point if within `low_sqr_d` and /// `up_sqr_d`. /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] p dim-long query point /// @param[in] low_sqr_d lower bound on squared distance, specified /// maximum squared distance /// @param[in] up_sqr_d current upper bounded on squared distance, /// current minimum squared distance (only consider distances less than /// this), see output. /// @param[out] i facet index corresponding to smallest distances /// @param[out] c closest point /// @return squared distance /// /// \pre currently assumes Elements are triangles regardless of /// dimension. template IGL_INLINE Scalar squared_distance( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & p, const Scalar low_sqr_d, const Scalar up_sqr_d, int & i, Eigen::PlainObjectBase & c) const; /// Compute squared distance to a query point (default `low_sqr_d`) /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] p dim-long query point /// @param[in] up_sqr_d current upper bounded on squared distance, /// current minimum squared distance (only consider distances less than /// this), see output. /// @param[out] i facet index corresponding to smallest distances /// @param[out] c closest point /// @return squared distance /// template IGL_INLINE Scalar squared_distance( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & p, const Scalar up_sqr_d, int & i, Eigen::PlainObjectBase & c) const; /// Intersect a ray with the mesh return all hits /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] origin dim-long ray origin /// @param[in] dir dim-long ray direction /// @param[out] hits list of hits /// @return true if any hits template IGL_INLINE bool intersect_ray( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & origin, const RowVectorDIMS & dir, std::vector & hits) const; /// Intersect a ray with the mesh return first hit /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] origin dim-long ray origin /// @param[in] dir dim-long ray direction /// @param[out] hit first hit /// @return true if any hit template IGL_INLINE bool intersect_ray( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & origin, const RowVectorDIMS & dir, igl::Hit & hit) const; /// Intersect a ray with the mesh return first hit farther than `min_t` /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] origin dim-long ray origin /// @param[in] dir dim-long ray direction /// @param[in] min_t minimum t value to consider /// @param[out] hit first hit /// @return true if any hit template IGL_INLINE bool intersect_ray( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & origin, const RowVectorDIMS & dir, const Scalar min_t, igl::Hit & hit) const; /// Compute the squared distance from all query points in P to the /// _closest_ points on the primitives stored in the AABB hierarchy for /// the mesh (V,Ele). /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] P #P by dim list of query points /// @param[out] sqrD #P list of squared distances /// @param[out] I #P list of indices into Ele of closest primitives /// @param[out] C #P by dim list of closest points template < typename DerivedEle, typename DerivedP, typename DerivedsqrD, typename DerivedI, typename DerivedC> IGL_INLINE void squared_distance( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const Eigen::MatrixBase & P, Eigen::PlainObjectBase & sqrD, Eigen::PlainObjectBase & I, Eigen::PlainObjectBase & C) const; /// Compute the squared distance from all query points in P already stored /// in its own AABB hierarchy to the _closest_ points on the primitives /// stored in the AABB hierarchy for the mesh (V,Ele). /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] other AABB hierarchy of another set of primitives (must be points) /// @param[in] other_V #other_V by dim list of query points /// @param[in] other_Ele #other_Ele by ss list of simplex indices into other_V /// (must be simple list of points: ss == 1) /// @param[out] sqrD #P list of squared distances /// @param[out] I #P list of indices into Ele of closest primitives /// @param[out] C #P by dim list of closest points template < typename DerivedEle, typename Derivedother_V, typename Derivedother_Ele, typename DerivedsqrD, typename DerivedI, typename DerivedC> IGL_INLINE void squared_distance( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const AABB & other, const Eigen::MatrixBase & other_V, const Eigen::MatrixBase & other_Ele, Eigen::PlainObjectBase & sqrD, Eigen::PlainObjectBase & I, Eigen::PlainObjectBase & C) const; private: template < typename DerivedEle, typename Derivedother_V, typename Derivedother_Ele, typename DerivedsqrD, typename DerivedI, typename DerivedC> IGL_INLINE Scalar squared_distance_helper( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const AABB * other, const Eigen::MatrixBase & other_V, const Eigen::MatrixBase& other_Ele, const Scalar up_sqr_d, Eigen::PlainObjectBase & sqrD, Eigen::PlainObjectBase & I, Eigen::PlainObjectBase & C) const; // Compute the squared distance to the primitive in this node: assumes // that this is indeed a leaf node. // // Inputs: // V #V by dim list of vertex positions // Ele #Ele by dim list of simplex indices // p dim-long query point // sqr_d current minimum distance for this query, see output // i current index into Ele of closest point, see output // c dim-long current closest point, see output // Outputs: // sqr_d minimum of initial value and squared distance to this // primitive // i possibly updated index into Ele of closest point // c dim-long possibly updated closest point template IGL_INLINE void leaf_squared_distance( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & p, const Scalar low_sqr_d, Scalar & sqr_d, int & i, Eigen::PlainObjectBase & c) const; // Default low_sqr_d template IGL_INLINE void leaf_squared_distance( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & p, Scalar & sqr_d, int & i, Eigen::PlainObjectBase & c) const; /// Intersect a ray with the mesh return all hits /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] origin dim-long ray origin /// @param[in] dir dim-long ray direction /// @param[out] hits list of hits /// @return true if any hits template IGL_INLINE bool intersect_ray_opt( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & origin, const RowVectorDIMS & dir, const RowVectorDIMS & inv_dir, const RowVectorDIMS & inv_dir_pad, std::vector & hits) const; /// Intersect a ray with the mesh return first hit farther than `min_t` /// /// @param[in] V #V by dim list of vertex positions /// @param[in] Ele #Ele by dim list of simplex indices /// @param[in] origin dim-long ray origin /// @param[in] dir dim-long ray direction /// @param[in] min_t minimum t value to consider /// @param[out] hit first hit /// @return true if any hit template IGL_INLINE bool intersect_ray_opt( const Eigen::MatrixBase & V, const Eigen::MatrixBase & Ele, const RowVectorDIMS & origin, const RowVectorDIMS & dir, const RowVectorDIMS & inv_dir, const RowVectorDIMS & inv_dir_pad, const Scalar min_t, igl::Hit & hit) const; public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; } #ifndef IGL_STATIC_LIBRARY # include "AABB.cpp" #endif #endif