pointkdtree_invariants.hpp

Back to Point kd-tree

pastel/geometry/pointkdtree/

#ifndef PASTELGEOMETRY_POINTKDTREE_INVARIANTS_HPP
#define PASTELGEOMETRY_POINTKDTREE_INVARIANTS_HPP

#include "pastel/geometry/pointkdtree/pointkdtree_invariants.h"

namespace Pastel
{

    namespace PointKdTree_
    {

        template <
            typename Settings, template <typename> class Customization,
            typename Locator = typename Settings::Locator,
            typename Real = typename Locator::Real,
            integer N = Locator::N>
        bool testInvariants(
            const PointKdTree<Settings, Customization>& tree,
            const typename PointKdTree<Settings, Customization>::Cursor& cursor,
            const AlignedBox<Real, N>& bound)
        {
            using Tree = PointKdTree<Settings, Customization>;
            using Fwd = Tree;
            PASTEL_FWD(Cursor);
            PASTEL_FWD(Point_ConstIterator);

            // The number of points in a node reported by 'cursor.points()'
            // must equal the distance between 'cursor.begin()' and 'cursor.end()'.
            if (REPORT_OP((std::distance(cursor.begin(), cursor.end())), !=, cursor.points()))
            {
                return false;
            }

            if (REPORT_OP(cursor.points(), <, 0))
            {
                return false;
            }

            // If a node is empty, then 'cursor.begin()' and 'cursor.end()' must
            // equal 'tree.end()'.
            if (REPORT(cursor.empty() && (cursor.begin() != tree.end() ||
                cursor.end() != tree.end())))
            {
                return false;
            }

            if (cursor.leaf())
            {
                // In a leaf node, the contained points must have that node
                // as the associated node.
                Point_ConstIterator iter = cursor.begin();
                Point_ConstIterator iterEnd = cursor.end();
                while(iter != iterEnd)
                {
                    Cursor leaf = iter->leaf();
                    if (REPORT(leaf != cursor))
                    {
                        return false;
                    }
                    ++iter;
                }
            }
            else
            {
                if (cursor != tree.root())
                {
                    if (cursor.min() > cursor.max() ||
                        // The minimum-bound must not decrease 
                        // when traversing down the tree.
                        cursor.min() < cursor.prevMin() ||
                        // The maximum-bound must not increase 
                        // when traversing down the tree.
                        cursor.max() > cursor.prevMax())
                    {
                        return false;
                    }
                }

                if (REPORT(cursor.splitPosition() < bound.min()[cursor.splitAxis()]))
                {
                    return false;
                }

                if (REPORT(cursor.splitPosition() > bound.max()[cursor.splitAxis()]))
                {
                    return false;
                }

                if (REPORT(cursor.left().points() + cursor.right().points() !=
                    cursor.points()))
                {
                    return false;
                }

                if (REPORT(cursor.left().parent() != cursor))
                {
                    return false;
                }

                if (REPORT(cursor.right().parent() != cursor))
                {
                    return false;
                }

                if (!cursor.empty())
                {
                    Point_ConstIterator begin = 
                        !cursor.left().empty() ? cursor.left().begin() : cursor.right().begin();
                    Point_ConstIterator end =
                        !cursor.right().empty() ? cursor.right().end() : cursor.left().end();
                    if (REPORT(cursor.begin() != begin || cursor.end() != end))
                    {
                        return false;
                    }
                }

                AlignedBox<Real, N> rightBound(bound);
                rightBound.min()[cursor.splitAxis()] = cursor.splitPosition();

                if (!testInvariants(tree, cursor.right(), rightBound))
                {
                    return false;
                }

                AlignedBox<Real, N> leftBound(bound);
                leftBound.max()[cursor.splitAxis()] = cursor.splitPosition();

                if (!testInvariants(tree, cursor.left(), leftBound))
                {
                    return false;
                }
            }

            return true;
        }

    }

    template <typename Settings, template <typename> class Customization>
    bool testInvariants(const PointKdTree<Settings, Customization>& tree)
    {
        using Fwd = Settings;
        PASTEL_FWD(Locator);
        using Real = typename Locator::Real;
        static constexpr integer N = Locator::N;

        AlignedBox<Real, N> bound(
            Vector<Real, N>(ofDimension(tree.n()), -Infinity()),
            Vector<Real, N>(ofDimension(tree.n()), Infinity()));;

        return PointKdTree_::testInvariants(tree, tree.root(), bound);
    }

}

#endif