search_nearby.h

Back to Nearest set

pastel/geometry/

// Description: Nearest neighbors searching in a kd-tree
// Documentation: nearestset.txt

#ifndef PASTELGEOMETRY_SEARCH_NEARBY_H
#define PASTELGEOMETRY_SEARCH_NEARBY_H

// Template concepts

#include "pastel/sys/indicator/indicator_concept.h"
#include "pastel/sys/output/output_concept.h"
#include "pastel/sys/point/point_concept.h"
#include "pastel/math/normbijection/normbijection_concept.h"
#include "pastel/geometry/nearestset/nearestset_concept.h"

// Template defaults

#include "pastel/math/normbijection/euclidean_normbijection.h"
#include "pastel/sys/indicator/all_indicator.h"
#include "pastel/sys/output/null_output.h"

// Implementation requirements

#include "pastel/geometry/distance/distance_point_point.h"

namespace Pastel
{

    //! Finds the nearest neighbors of a point in a nearest-set.
    /*!
   nearestSet (NearestSet):
   The nearest-set in which to search neighbors in.

   searchPoint (Point):
   The point for which to search a neighbor for.

   Optional arguments
   ------------------

   accept (Indicator(PointId)):
   An indicator which decides whether to accept a point 
   as a neighbor or not.
   Default: allIndicator()

   maxDistance2 (Real >= 0):
   The distance after which points are not considered neighbors
   anymore. Can be set to (Real)Infinity(). This distance
   is in terms of the used norm bijection.
   Default: (Real)Infinity()

   report (Output(Real, PointId)):
   An output to which the found neighbors 
   are reported to. The reporting is done in the 
   form report(distance, point). The reporter
   must return a boolean on¨whether points beyond 
   the reported distance should be culled.
   Even if culling is indicated, the algorithm 
   may still report some points beyond the reported 
   distance; 
   Default: nullOutput()

   normBijection (NormBijection):
   The norm used to measure distance.

   protection (Real >= 1):
   A factor by which the reported distance is multiplied
   to get the culling distance, in case culling is indicated
   by the reporter. This guarantees robustness in case
   of close-to-equal distances (e.g. points on a sphere).
   Default: normBijection.scalingFactor(1.01)

   returns (std::pair<Real, PointId>)
   ----------------------------------
   
   The first element is the distance 
   (in terms of the norm bijection) to the farthest
   point for which the reporter returns true.
   If no such point exists, (Real)Infinity().
   
   The second element is the farthest point for which
   the reporter returns true. If no such point exists,
   *begin(nearestSet.pointSet()).
   */
    template <
        typename NearestSet,
        typename Search_Point,
        typename... ArgumentSet,
        Requires<
            Models<NearestSet, NearestSet_Concept>,
            Models<Search_Point, Point_Concept>
        > = 0
    >
    void searchNearby(
        const NearestSet& nearestSet,
        const Search_Point& searchPoint,
        ArgumentSet&&... argumentSet)
    {
        using std::begin;

        using PointId = PointSet_PointId<NearestSet>;
        using Real = Point_Real<Search_Point>;

        auto&& report = 
            PASTEL_ARG(
                report, 
                []() {return nullOutput();},
                [](auto input) {return std::true_type();}
            );

        auto&& accept = 
            PASTEL_ARG(
                accept, 
                []() {return allIndicator();},
                [](auto input) 
                {
                    return Models<decltype(input), 
                        Indicator_Concept(PointId)>();
                }
            );

        auto&& normBijection = 
            PASTEL_ARG(
                normBijection, 
                []() {return Euclidean_NormBijection<real>();},
                [](auto input) {return implicitArgument(
                    Models<decltype(input), NormBijection_Concept>());}
            );

        const Real maxDistance2 = PASTEL_ARG_S(
            maxDistance2, 
            (Real)Infinity());

        const Real protection = PASTEL_ARG_S(
            protection, 
            normBijection.scalingFactor(1.01));

        ENSURE_OP(maxDistance2, >=, 0);
        ENSURE_OP(protection, >=, 1);

        if (maxDistance2 == 0)
        {
            // There is nothing to search for.
            // Note that we consider the search-ball open.
            return;
        }

        auto searchBruteForce = [&](
            auto&& pointIdSet,
            Real cullDistance2)
        {
            RANGES_FOR(auto&& pointId, pointIdSet)
            {
                // Compute the distance from the node-point
                // to the search-point.
                const Real currentDistance2 = 
                    distance2(
                        nearestSet.asPoint(pointId),
                        searchPoint,
                        PASTEL_TAG(normBijection),
                        normBijection,
                        PASTEL_TAG(keepGoing),
                        // Stop computing the distance if it exceeds
                        // the culling distance.
                        [&](auto&& that) {return that < cullDistance2;}
                    );

                // Reject the point if the user rejects it or it
                // is farther than the culling distance.
                if (currentDistance2 >= cullDistance2 || !accept(pointId))
                {
                    continue;
                }

                // Note that if there are multiple points at the same 
                // distance, then the points after the first should _not_
                // be culled away. We deal with this by expanding the 
                // suggested culling radius by a protective factor.
                const Real cullSuggestion2 =
                    report(currentDistance2, pointId) * protection;
                if (cullSuggestion2 < cullDistance2)
                {
                    // The cull-radius got smaller; update it.
                    cullDistance2 = cullSuggestion2;
                }
            }

            // Return the updated cull-distance.
            return cullDistance2;
        };

        nearestSet.findNearbyPointsets(
            searchPoint,
            normBijection,
            maxDistance2,
            searchBruteForce);
    }

}

#endif