<commit_before>#ifndef GF2_SEGMENTATION_HPP
#define GF2_SEGMENTATION_HPP

#include "globfit2/optimization/segmentation.h"
#include "globfit2/my_types.h"                  // PCLPointAllocator
#include <vector>

#include "boost/filesystem.hpp"

#if GF2_USE_PCL
#   include "pcl/point_types.h"
#   include <pcl/point_cloud.h>
#   include "pcl/console/parse.h"
#endif

#include "pcltools/util.hpp"

#include "globfit2/util/containers.hpp" // add( map, gid, primitive), add( vector, gid, primitive )
#include "globfit2/parameters.h"        // CandidateGeneratorParams
#include "globfit2/processing/util.hpp" // getNeighbourIndices
#include "globfit2/util/diskUtil.hpp"   // saveBackup
#include "globfit2/io/io.h"             // readPoints
#include "globfit2/optimization/patchDistanceFunctors.h" // RepresentativeSqrPatchPatchDistanceFunctorT


namespace GF2 {

//! \param[in,out] points
template < class    _PointPrimitiveT
         , class    _PrimitiveT
         , typename _Scalar
         , class    _PointContainerT
         >
int
Segmentation::orientPoints( _PointContainerT          &points
                          , _Scalar             const  scale
                          , int                 const  nn_K   )
{
    typedef pcl::PointCloud<pcl::PointXYZ>        CloudXYZ;

    // to pcl cloud
    CloudXYZ::Ptr cloud( new CloudXYZ() );
    _PointPrimitiveT::template toCloud<CloudXYZ::Ptr, _PointContainerT, PCLPointAllocator<_PointPrimitiveT::Dim> >
            ( cloud, points );

    // (1) local fit lines from pcl cloud
    std::vector<_PrimitiveT> fit_lines;
    std::vector<int        > point_ids;
    {
        fitLocal( /* [out]       lines: */ fit_lines
               , /*            points: */ cloud
               , /*           indices: */ NULL
               , /*              nn_K: */ nn_K
               , /*         nn_radius: */ scale
               , /*       soft_radius: */ true
               , /* [out]     mapping: */ &point_ids ); // contains point id for fit_line

        // copy line direction into point
        for ( int pid_id = 0; pid_id != point_ids.size(); ++pid_id )
        {
            const int pid = point_ids[pid_id];
            points[pid].coeffs().template segment<3>(3) = fit_lines.at(pid).dir();
        }
    } // ... (1) local fit

    return EXIT_SUCCESS;
} //...Segmentation::orientPoints()

/*! \brief Fits a local direction to each point and it's neighourhood.
 *  \tparam PrimitiveContainerT Concept: vector< vector< LinePrimitive2/PlanePrimitive > >.
 *  \tparam _PointContainerPtrT Concept: pcl::PointCloud<pcl::PointXYZRGB>::Ptr.
 */
template < class _PrimitiveContainerT
         , class _PointContainerPtrT> int
Segmentation:: fitLocal( _PrimitiveContainerT        & primitives
                       , _PointContainerPtrT    const  cloud
                       , std::vector<int>       const* indices
                       , int                    const  K
                       , float                  const  radius
                       , bool                   const  soft_radius
                       , std::vector<int>            * point_ids
                       )
{
    using std::vector;
    typedef typename _PrimitiveContainerT::value_type   PrimitiveT;
    typedef typename PrimitiveT::Scalar                 Scalar;
    typedef typename _PointContainerPtrT::element_type  PointsT;

    if ( indices ) { std::cerr << __PRETTY_FUNCTION__ << "]: indices must be NULL, not implemented yet..." << std::endl; return EXIT_FAILURE; }

    // get neighbourhoods
    std::vector< std::vector<int   > > neighs;
    std::vector< std::vector<Scalar> > sqr_dists;
    processing::getNeighbourhoodIndices( /*   [out] neighbours: */ neighs
                                       , /* [in]  pointCloud: */ cloud
                                       , /* [in]     indices: */ indices
                                       , /* [out]  sqr_dists: */ &sqr_dists
                                       , /* [in]        nn_K: */ K              // 5
                                       , /* [in]      radius: */ radius         // 0.02f
                                       , /* [in] soft_radius: */ soft_radius    // true
                                       );

    // only use, if more then 2 data-points
    if ( std::count_if( neighs.begin(), neighs.end(), [] (vector<int> const& n1) { return n1.size() > 2; } ) < 2 )
    {
        std::cerr << "[" << __func__ << "]: " << "not enough to work with (<2)...change scale " << radius << std::endl;
        return EXIT_SUCCESS;
    }

    // every point proposes primitive[s] using its neighbourhood
    int skipped = 0;
    for ( size_t pid = 0; pid != neighs.size(); ++pid )
    {
        // can't fit a line to 0 or 1 points
        if ( neighs[pid].size() < 2 )
        {
            ++skipped;
            continue;
        }

        int err = EXIT_SUCCESS;
        if ( PrimitiveT::EmbedSpaceDim == 2 ) // we are in 2D, and TLine is LinePrimitive2
        {
            Eigen::Matrix<Scalar,6,1> line;
            err = smartgeometry::geometry::fitLinearPrimitive<PointsT,Scalar,6>( /*           output: */ line , /*         points: */ *cloud
                                                                                          , /*          scale: */ radius
                                                                                          , /*        indices: */ &(neighs[pid])
                                                                                          , /*    refit times: */ 2
                                                                                          , /* use input line: */ false
                                                                                          );
            if ( err == EXIT_SUCCESS )
            {
                // Create a LinePrimitive from its coeffs <x0, dir>
                primitives.emplace_back( PrimitiveT(line) );
            }
        }
        else // we are in 3D, and TLine is PlanePrimitive
        {
//            std::cout << "fitting to " << (*cloud)[pid].getVector3fMap().transpose() << " and neighbours:\n";
//            for ( int j = 0; j != neighs[pid].size(); ++j )
//                std::cout << "\t" << (*cloud)[ neighs[pid][j] ].getVector3fMap().transpose() << "\n";

            // fitLInearPirmitive uses "rows==4" to fit a plane TODO: use processing::fitlinearprimitive instead.
            Eigen::Matrix<Scalar,4,1> plane;
            err = smartgeometry::geometry::fitLinearPrimitive<PointsT,Scalar,4>( /*         output: */ plane
                                                                               , /*         points: */ *cloud
                                                                               , /*          scale: */ radius
                                                                               , /*        indices: */ &(neighs[pid])
                                                                               , /*    refit times: */ 2
                                                                               , /* use input line: */ false
                                                                               );
            if ( err == EXIT_SUCCESS )
            {
                // Create a PlanePrimitive from < n, d > format
                // by using n, and the center point of the neighbourhood.
                primitives.emplace_back(  PrimitiveT( /*     x0: */ (*cloud)[pid].getVector3fMap()
                                                    , /* normal: */ plane.template head<3>() )  );
#if 0
                std::cout << "fit " << primitives.back().toString() << " to\n";
                for ( int i = 0; i != neighs[pid].size(); ++i )
                {
                    const int pj = neighs[pid][i];
                    std::cout << (*cloud)[pj].getVector3fMap().transpose()
                              << ", with dist " << sqr_dists[pid][i] << " == " << sqr_dists[pid][i] << " < " << radius << std::endl;
                }
#endif
            }
        }


        if ( point_ids )
        {
            point_ids->emplace_back( pid );
        }
    } //...for points

    std::cout << "[" << __func__ << "]: "
              << skipped << "/" << neighs.size() << ": " << skipped / static_cast<float>(neighs.size()) * 100.f << "% of points did not produce primitives, so the primitive count is:"
              << primitives.size() << " = " << primitives.size() / static_cast<float>(neighs.size()) *100.f << "%" << std::endl;

    return EXIT_SUCCESS;
} // ...Segment::propose()

/*
 * \brief Groups unoriented points into oriented patches represented by a single primitive
 *                   (1) group to patches
 *                   (2) refit lines to patches
 * \tparam _PrimitiveContainerT Concept: std::map< int, std::vector<_PrimitiveT> >. Groups primitives by their GID.
 * \param[out] patches                   Ready to process patches, each of them with one direction only. ( { 0: [Primitive00] }, { 1: [Primitive11] }, ... )
 * \param[in/out] points                 Input points that get assigned to the patches by their GID tag set.
 * \param[in]  scale                     Spatial scale to use for fits.
 * \param[in]  angles                    Desired angles to use for groupings.
 * \param[in]  patchPatchDistanceFunctor #regionGrow() uses the thresholds encoded to group points. The evalSpatial() function is used to assign orphan points.
 * \param[in]  nn_K                      Number of nearest neighbour points looked for in #regionGrow().
 */
template < class       _PrimitiveT
         , typename    _Scalar
         , class       _PrimitiveContainerT
         , class       _PointContainerT
         , class       _PatchPatchDistanceFunctorT
         > int
Segmentation::patchify( _PrimitiveContainerT                   & patches
                      , _PointContainerT                       & points
                      , _Scalar                           const  scale
                      , std::vector<_Scalar>              const& angles
                      , _PatchPatchDistanceFunctorT       const& patchPatchDistanceFunctor
                      , int                               const  nn_K
                      )
{
    typedef segmentation::Patch<_Scalar,_PrimitiveT> PatchT;
    typedef std::vector< PatchT >                    PatchesT;

    typedef typename _PointContainerT::value_type PointPrimitiveT;

    // log
    std::cout << "[" << __func__ << "]: " << "PatchPatchDistance by " << patchPatchDistanceFunctor.toString() << std::endl;

    // (1) group
    PatchesT groups;
    {
        regionGrow<_PrimitiveT>
                  ( /* [in,out]  points/pointsWGIDTag: */ points
                  , /* [out]          groups_pointids: */ groups
                  , /* [in]                     scale: */ scale
                  , /* [in] patchPatchDistanceFunctor: */ patchPatchDistanceFunctor
                  , /* [in]              gid_tag_name: */ PointPrimitiveT::GID
                  , /* [in]                      nn_K: */ nn_K );
    } // ... (1) group

    // (2) Create PrimitiveContainer
    // calc populations
    GidPidVectorMap populations;
    processing::getPopulations( populations, points );

    // Copy the representative direction of each patch in groups to an output patch with GID as it's linear index in groups.
    for ( size_t gid = 0; gid != groups.size(); ++gid )
    {

        if ( _PrimitiveT::EmbedSpaceDim == 2) // added by Aron on 17 Sep 2014
        {
            // LINE
        containers::add( patches, gid, groups[gid].getRepresentative() )
                .setTag( _PrimitiveT::GID    , gid )
                .setTag( _PrimitiveT::DIR_GID, gid );
        }
        else if ( _PrimitiveT::EmbedSpaceDim == 3)
        {
            // PLANE
            _PrimitiveT toAdd;
            processing::fitLinearPrimitive<_PrimitiveT::Dim>( /*  [in,out] primitive: */ toAdd
                                                            , /*              points: */ points
                                                            , /*               scale: */ scale
                                                            , /*             indices: */ &(populations[gid])
                                                            , /*    refit iter count: */ 2                   // fit and refit twice
                                                            , /*    start from input: */ (_PrimitiveT*)NULL  // use to calculate initial weights
                                                            , /* refit position only: */ false
                                                            , /*               debug: */ false  );
            std::cout << "repr: " << groups[gid].getRepresentative().toString()
                      << "refit: " << toAdd.toString() << std::endl;

            containers::add( patches, gid, toAdd /*groups[gid].getRepresentative()*/ )
                    .setTag( _PrimitiveT::GID    , gid )
                    .setTag( _PrimitiveT::DIR_GID, gid );
        }
        else
            std::cerr << "[" << __func__ << "]: " << "Unrecognized EmbedSpaceDim - refit to patch did not work" << std::endl;
    }

    return EXIT_SUCCESS;
} // ...Segmentation::patchify()

/*  \brief                               Greedy region growing
 *  \tparam _PrimitiveContainerT         Concept: std::vector<\ref GF2::LinePrimitive2>
 *  \tparam _PointContainerT             Concept: std::vector<\ref GF2::PointPrimitive>
 *  \tparam _PointPatchDistanceFunctorT  Concept: \ref RepresentativeSqrPatchPatchDistanceFunctorT.
 *  \tparam _PatchesT                    Concept: vector< \ref segmentation::Patch <_Scalar,_PrimitiveT> >
 *  \tparam _PrimitiveT                  Concept: \ref GF2::LinePrimitive2
 *  \tparam _Scalar                      Concept: float
 *  \tparam _PointT                      Concept: \ref GF2::PointPrimitive
 *  \param[in,out] points                Input points to create patches from. The \p gid_tag_name field of the points will be set according to their patch assignment.
 *  \param[out] groups_arg               Holds the point-id-groups, that can then be refit to to get a patch location and direction. Concept: vector<\ref segmentation::Patch>.
 *  \param[in] scale                     Spatial extent of the input. In practice unused, since the \p patchPatchDistanceFunctor was constructed with it.
 *  \param[in] patchPatchDistanceFunctor Takes two patches, and decides, whether they are similar enough to be merged.
 *                                       In practice, takes a patch that is currently grown, and a temporary patch
 *                                       that only contains a neighbouring point, and decides. See in \ref RepresentativeSqrPatchPatchDistanceFunctorT.
 *  \param[in] gid_tag_name              The key value of GID in _PointT. Suggested to be: _PointT::GID.
 *  \param[in] nn_K                      Number of nearest neighbour points looked for.<commit_msg>- fixed 0 candidates in segmentation.hpp<commit_after>#ifndef GF2_SEGMENTATION_HPP
#define GF2_SEGMENTATION_HPP

#include "globfit2/optimization/segmentation.h"
#include "globfit2/my_types.h"                  // PCLPointAllocator
#include <vector>

#include "boost/filesystem.hpp"

#if GF2_USE_PCL
#   include "pcl/point_types.h"
#   include <pcl/point_cloud.h>
#   include "pcl/console/parse.h"
#endif

#include "pcltools/util.hpp"

#include "globfit2/util/containers.hpp" // add( map, gid, primitive), add( vector, gid, primitive )
#include "globfit2/parameters.h"        // CandidateGeneratorParams
#include "globfit2/processing/util.hpp" // getNeighbourIndices
#include "globfit2/util/diskUtil.hpp"   // saveBackup
#include "globfit2/io/io.h"             // readPoints
#include "globfit2/optimization/patchDistanceFunctors.h" // RepresentativeSqrPatchPatchDistanceFunctorT


namespace GF2 {

//! \param[in,out] points
template < class    _PointPrimitiveT
         , class    _PrimitiveT
         , typename _Scalar
         , class    _PointContainerT
         >
int
Segmentation::orientPoints( _PointContainerT          &points
                          , _Scalar             const  scale
                          , int                 const  nn_K   )
{
    typedef pcl::PointCloud<pcl::PointXYZ>        CloudXYZ;

    // to pcl cloud
    CloudXYZ::Ptr cloud( new CloudXYZ() );
    _PointPrimitiveT::template toCloud<CloudXYZ::Ptr, _PointContainerT, PCLPointAllocator<_PointPrimitiveT::Dim> >
            ( cloud, points );

    // (1) local fit lines from pcl cloud
    std::vector<_PrimitiveT> fit_lines;
    std::vector<int        > point_ids;
    {
        fitLocal( /* [out]       lines: */ fit_lines
               , /*            points: */ cloud
               , /*           indices: */ NULL
               , /*              nn_K: */ nn_K
               , /*         nn_radius: */ scale
               , /*       soft_radius: */ true
               , /* [out]     mapping: */ &point_ids ); // contains point id for fit_line

        // copy line direction into point
        for ( int pid_id = 0; pid_id != point_ids.size(); ++pid_id )
        {
            const int pid = point_ids[pid_id];
            points[pid].coeffs().template segment<3>(3) = fit_lines.at(pid).dir();
        }
    } // ... (1) local fit

    return EXIT_SUCCESS;
} //...Segmentation::orientPoints()

/*! \brief Fits a local direction to each point and it's neighourhood.
 *  \tparam PrimitiveContainerT Concept: vector< vector< LinePrimitive2/PlanePrimitive > >.
 *  \tparam _PointContainerPtrT Concept: pcl::PointCloud<pcl::PointXYZRGB>::Ptr.
 */
template < class _PrimitiveContainerT
         , class _PointContainerPtrT> int
Segmentation:: fitLocal( _PrimitiveContainerT        & primitives
                       , _PointContainerPtrT    const  cloud
                       , std::vector<int>       const* indices
                       , int                    const  K
                       , float                  const  radius
                       , bool                   const  soft_radius
                       , std::vector<int>            * point_ids
                       )
{
    using std::vector;
    typedef typename _PrimitiveContainerT::value_type   PrimitiveT;
    typedef typename PrimitiveT::Scalar                 Scalar;
    typedef typename _PointContainerPtrT::element_type  PointsT;

    if ( indices ) { std::cerr << __PRETTY_FUNCTION__ << "]: indices must be NULL, not implemented yet..." << std::endl; return EXIT_FAILURE; }

    // get neighbourhoods
    std::vector< std::vector<int   > > neighs;
    std::vector< std::vector<Scalar> > sqr_dists;
    processing::getNeighbourhoodIndices( /*   [out] neighbours: */ neighs
                                       , /* [in]  pointCloud: */ cloud
                                       , /* [in]     indices: */ indices
                                       , /* [out]  sqr_dists: */ &sqr_dists
                                       , /* [in]        nn_K: */ K              // 5
                                       , /* [in]      radius: */ radius         // 0.02f
                                       , /* [in] soft_radius: */ soft_radius    // true
                                       );

    // only use, if more then 2 data-points
    if ( std::count_if( neighs.begin(), neighs.end(), [] (vector<int> const& n1) { return n1.size() > 2; } ) < 2 )
    {
        std::cerr << "[" << __func__ << "]: " << "not enough to work with (<2)...change scale " << radius << std::endl;
        return EXIT_SUCCESS;
    }

    // every point proposes primitive[s] using its neighbourhood
    int skipped = 0;
    for ( size_t pid = 0; pid != neighs.size(); ++pid )
    {
        // can't fit a line to 0 or 1 points
        if ( neighs[pid].size() < 2 )
        {
            ++skipped;
            continue;
        }

        int err = EXIT_SUCCESS;
        if ( PrimitiveT::EmbedSpaceDim == 2 ) // we are in 2D, and TLine is LinePrimitive2
        {
            Eigen::Matrix<Scalar,6,1> line;
            err = smartgeometry::geometry::fitLinearPrimitive<PointsT,Scalar,6>( /*           output: */ line , /*         points: */ *cloud
                                                                                          , /*          scale: */ radius
                                                                                          , /*        indices: */ &(neighs[pid])
                                                                                          , /*    refit times: */ 2
                                                                                          , /* use input line: */ false
                                                                                          );
            if ( err == EXIT_SUCCESS )
            {
                // Create a LinePrimitive from its coeffs <x0, dir>
                primitives.emplace_back( PrimitiveT(line) );
            }
        }
        else // we are in 3D, and TLine is PlanePrimitive
        {
//            std::cout << "fitting to " << (*cloud)[pid].getVector3fMap().transpose() << " and neighbours:\n";
//            for ( int j = 0; j != neighs[pid].size(); ++j )
//                std::cout << "\t" << (*cloud)[ neighs[pid][j] ].getVector3fMap().transpose() << "\n";

            // fitLInearPirmitive uses "rows==4" to fit a plane TODO: use processing::fitlinearprimitive instead.
            Eigen::Matrix<Scalar,4,1> plane;
            err = smartgeometry::geometry::fitLinearPrimitive<PointsT,Scalar,4>( /*         output: */ plane
                                                                               , /*         points: */ *cloud
                                                                               , /*          scale: */ radius
                                                                               , /*        indices: */ &(neighs[pid])
                                                                               , /*    refit times: */ 2
                                                                               , /* use input line: */ false
                                                                               );
            if ( err == EXIT_SUCCESS )
            {
                // Create a PlanePrimitive from < n, d > format
                // by using n, and the center point of the neighbourhood.
                primitives.emplace_back(  PrimitiveT( /*     x0: */ (*cloud)[pid].getVector3fMap()
                                                    , /* normal: */ plane.template head<3>() )  );
#if 0
                std::cout << "fit " << primitives.back().toString() << " to\n";
                for ( int i = 0; i != neighs[pid].size(); ++i )
                {
                    const int pj = neighs[pid][i];
                    std::cout << (*cloud)[pj].getVector3fMap().transpose()
                              << ", with dist " << sqr_dists[pid][i] << " == " << sqr_dists[pid][i] << " < " << radius << std::endl;
                }
#endif
            }
        }


        if ( point_ids )
        {
            point_ids->emplace_back( pid );
        }
    } //...for points

    std::cout << "[" << __func__ << "]: "
              << skipped << "/" << neighs.size() << ": " << skipped / static_cast<float>(neighs.size()) * 100.f << "% of points did not produce primitives, so the primitive count is:"
              << primitives.size() << " = " << primitives.size() / static_cast<float>(neighs.size()) *100.f << "%" << std::endl;

    return EXIT_SUCCESS;
} // ...Segment::propose()

/*
 * \brief Groups unoriented points into oriented patches represented by a single primitive
 *                   (1) group to patches
 *                   (2) refit lines to patches
 * \tparam _PrimitiveContainerT Concept: std::map< int, std::vector<_PrimitiveT> >. Groups primitives by their GID.
 * \param[out] patches                   Ready to process patches, each of them with one direction only. ( { 0: [Primitive00] }, { 1: [Primitive11] }, ... )
 * \param[in/out] points                 Input points that get assigned to the patches by their GID tag set.
 * \param[in]  scale                     Spatial scale to use for fits.
 * \param[in]  angles                    Desired angles to use for groupings.
 * \param[in]  patchPatchDistanceFunctor #regionGrow() uses the thresholds encoded to group points. The evalSpatial() function is used to assign orphan points.
 * \param[in]  nn_K                      Number of nearest neighbour points looked for in #regionGrow().
 */
template < class       _PrimitiveT
         , typename    _Scalar
         , class       _PrimitiveContainerT
         , class       _PointContainerT
         , class       _PatchPatchDistanceFunctorT
         > int
Segmentation::patchify( _PrimitiveContainerT                   & patches
                      , _PointContainerT                       & points
                      , _Scalar                           const  scale
                      , std::vector<_Scalar>              const& angles
                      , _PatchPatchDistanceFunctorT       const& patchPatchDistanceFunctor
                      , int                               const  nn_K
                      )
{
    typedef segmentation::Patch<_Scalar,_PrimitiveT> PatchT;
    typedef std::vector< PatchT >                    PatchesT;

    typedef typename _PointContainerT::value_type PointPrimitiveT;

    // log
    std::cout << "[" << __func__ << "]: " << "PatchPatchDistance by " << patchPatchDistanceFunctor.toString() << std::endl;

    // (1) group
    PatchesT groups;
    {
        regionGrow<_PrimitiveT>
                  ( /* [in,out]  points/pointsWGIDTag: */ points
                  , /* [out]          groups_pointids: */ groups
                  , /* [in]                     scale: */ scale
                  , /* [in] patchPatchDistanceFunctor: */ patchPatchDistanceFunctor
                  , /* [in]              gid_tag_name: */ PointPrimitiveT::GID
                  , /* [in]                      nn_K: */ nn_K );
    } // ... (1) group

    // (2) Create PrimitiveContainer
    // calc populations
    GidPidVectorMap populations;
    processing::getPopulations( populations, points );

    // Copy the representative direction of each patch in groups to an output patch with GID as it's linear index in groups.
    for ( size_t gid = 0; gid != groups.size(); ++gid )
    {

        if ( _PrimitiveT::EmbedSpaceDim == 2) // added by Aron on 17 Sep 2014
        {
            // LINE
        containers::add( patches, gid, groups[gid].getRepresentative() )
                .setTag( _PrimitiveT::GID    , gid )
                .setTag( _PrimitiveT::DIR_GID, gid );
        }
        else if ( _PrimitiveT::EmbedSpaceDim == 3)
        {
            // PLANE
            _PrimitiveT toAdd;
            int err = processing::fitLinearPrimitive<_PrimitiveT::Dim>( /*  [in,out] primitive: */ toAdd
                                                            , /*              points: */ points
                                                            , /*               scale: */ scale
                                                            , /*             indices: */ &(populations[gid])
                                                            , /*    refit iter count: */ 2                   // fit and refit twice
                                                            , /*    start from input: */ (_PrimitiveT*)NULL  // use to calculate initial weights
                                                            , /* refit position only: */ false
                                                            , /*               debug: */ false  );
            std::cout << "repr: " << groups[gid].getRepresentative().toString()
                      << "refit: " << toAdd.toString() << std::endl;
            if ( err != EXIT_SUCCESS )
            {
                std::cerr << "[" << __func__ << "]: " << "Vaiiii? err: " << err << ", pop: " << populations[gid].size() << std::endl;
                toAdd = groups[gid].getRepresentative();
                if ( toAdd.template dir().norm() < 0.9 )
                {
                    std::cerr << "[" << __func__ << "]: " << "getRepr().norm( " << toAdd.template dir().norm() << ") < 0.9: " << toAdd.toString() << ", this is very bad news" << std::endl;
                }
//                containers::add( patches, gid, toAdd )
//                        .setTag( _PrimitiveT::GID    , gid )
//                        .setTag( _PrimitiveT::DIR_GID, gid );
            }
            else
            {
                if ( toAdd.template dir().norm() < 0.9 )
                {
                    std::cerr << "[" << __func__ << "]: " << "toAdd.norm( " << toAdd.template dir().norm() << ") < 0.9: " << toAdd.toString() << ", this is very bad news" << std::endl;
                    throw new std::runtime_error("adding primitive with too small normal");
                }

                containers::add( patches, gid, toAdd /*groups[gid].getRepresentative()*/ )
                        .setTag( _PrimitiveT::GID    , gid )
                        .setTag( _PrimitiveT::DIR_GID, gid );
            }
        }
        else
            std::cerr << "[" << __func__ << "]: " << "Unrecognized EmbedSpaceDim - refit to patch did not work" << std::endl;
    }

    return EXIT_SUCCESS;
} // ...Segmentation::patchify()

/*  \brief                               Greedy region growing
 *  \tparam _PrimitiveContainerT         Concept: std::vector<\ref GF2::LinePrimitive2>
 *  \tparam _PointContainerT             Concept: std::vector<\ref GF2::PointPrimitive>
 *  \tparam _PointPatchDistanceFunctorT  Concept: \ref RepresentativeSqrPatchPatchDistanceFunctorT.
 *  \tparam _PatchesT                    Concept: vector< \ref segmentation::Patch <_Scalar,_PrimitiveT> >
 *  \tparam _PrimitiveT                  Concept: \ref GF2::LinePrimitive2
 *  \tparam _Scalar                      Concept: float
 *  \tparam _PointT                      Concept: \ref GF2::PointPrimitive
 *  \param[in,out] points                Input points to create patches from. The \p gid_tag_name field of the points will be set according to their patch assignment.
 *  \param[out] groups_arg               Holds the point-id-groups, that can then be refit to to get a patch location and direction. Concept: vector<\ref segmentation::Patch>.
 *  \param[in] scale                     Spatial extent of the input. In practice unused, since the \p patchPatchDistanceFunctor was constructed with it.
 *  \param[in] patchPatchDistanceFunctor Takes two patches, and decides, whether they are similar enough to be merged.
 *                                       In practice, takes a patch that is currently grown, and a temporary patch
 *                                       that only contains a neighbouring point, and decides. See in \ref RepresentativeSqrPatchPatchDistanceFunctorT.
 *  \param[in] gid_tag_name              The key value of GID in _PointT. Suggested to be: _PointT::GID.
 *  \param[in] nn_K                      Number of nearest neighbour points looked for.
{'fraction_non_alphanumeric': 0.11175417279529322, 'fraction_numerical': 0.005979407814798235, 'mean_word_length': 1.3458595859585958, 'pattern_counts': {'":': 2, '<': 375, '<?xml version=': 0, '>': 91, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 180, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2494134', 'n_tokens_mistral': 8178, 'n_tokens_neox': 7833, 'n_words': 2038}